@inflexa-ai/harness 0.42.2 → 0.43.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.
Files changed (199) hide show
  1. package/README.md +2 -2
  2. package/dist/agents/sandbox/shared.d.ts +1 -6
  3. package/dist/agents/sandbox/shared.js +0 -15
  4. package/dist/app/chat-turn.js +13 -1
  5. package/dist/config/environment-stores.d.ts +4 -2
  6. package/dist/contracts/chat-events.d.ts +9 -1
  7. package/dist/contracts/chat-frame.d.ts +3 -3
  8. package/dist/contracts/chat-frame.js +8 -3
  9. package/dist/contracts/chat-parts.d.ts +4 -1
  10. package/dist/contracts/data-profile.d.ts +4 -6
  11. package/dist/contracts/index.d.ts +1 -1
  12. package/dist/contracts/report-blocks.d.ts +1364 -75
  13. package/dist/contracts/report-blocks.js +221 -29
  14. package/dist/contracts/report-reference.js +1 -1
  15. package/dist/contracts/schemas/chat-events.d.ts +13 -0
  16. package/dist/contracts/schemas/chat-events.js +5 -0
  17. package/dist/contracts/schemas/chat-parts.d.ts +2 -2
  18. package/dist/contracts/schemas/index.d.ts +1 -1
  19. package/dist/contracts/schemas/index.js +1 -1
  20. package/dist/execution/input-signature.d.ts +7 -13
  21. package/dist/execution/input-signature.js +0 -0
  22. package/dist/execution/run-canceler.d.ts +3 -2
  23. package/dist/execution/run-canceler.js +3 -2
  24. package/dist/execution/run-event-stream.d.ts +16 -2
  25. package/dist/execution/run-event-stream.js +54 -3
  26. package/dist/index.d.ts +1 -8
  27. package/dist/index.js +5 -16
  28. package/dist/input-scan/enrich.d.ts +0 -2
  29. package/dist/input-scan/enrich.js +0 -1
  30. package/dist/lib/metrics.js +6 -7
  31. package/dist/lib/otel-spans.d.ts +4 -9
  32. package/dist/lib/otel-spans.js +3 -8
  33. package/dist/loop/run-agent.d.ts +4 -2
  34. package/dist/loop/run-agent.js +38 -23
  35. package/dist/memory/thread-history.d.ts +1 -1
  36. package/dist/prompts/report-session.d.ts +1 -1
  37. package/dist/prompts/report-session.js +17 -0
  38. package/dist/provenance/exec-frame.d.ts +1 -1
  39. package/dist/report-model/authoring-grammar.d.ts +416 -17
  40. package/dist/report-model/authoring-grammar.js +25 -4
  41. package/dist/report-model/block-walk.d.ts +20 -4
  42. package/dist/report-model/block-walk.js +64 -7
  43. package/dist/report-model/draft-finish.d.ts +6 -2
  44. package/dist/report-model/draft-finish.js +6 -1
  45. package/dist/report-model/production-resolver.d.ts +8 -0
  46. package/dist/report-model/production-resolver.js +1 -1
  47. package/dist/report-model/validate.d.ts +20 -10
  48. package/dist/report-model/validate.js +29 -13
  49. package/dist/report-render/chart-export.d.ts +52 -0
  50. package/dist/report-render/chart-export.js +192 -0
  51. package/dist/report-render/chart-presets.d.ts +46 -47
  52. package/dist/report-render/chart-presets.js +31 -101
  53. package/dist/report-render/chart-renderers.d.ts +163 -0
  54. package/dist/report-render/chart-renderers.js +1055 -0
  55. package/dist/report-render/chart-toolbox.d.ts +124 -0
  56. package/dist/report-render/chart-toolbox.js +595 -0
  57. package/dist/report-render/chart.d.ts +60 -27
  58. package/dist/report-render/chart.js +2323 -524
  59. package/dist/report-render/design.d.ts +476 -16
  60. package/dist/report-render/design.js +390 -56
  61. package/dist/report-render/figures/common.d.ts +321 -0
  62. package/dist/report-render/figures/common.js +759 -0
  63. package/dist/report-render/figures/dendrogram.d.ts +36 -0
  64. package/dist/report-render/figures/dendrogram.js +130 -0
  65. package/dist/report-render/figures/dense.d.ts +111 -0
  66. package/dist/report-render/figures/dense.js +204 -0
  67. package/dist/report-render/figures/dotplot.d.ts +28 -0
  68. package/dist/report-render/figures/dotplot.js +234 -0
  69. package/dist/report-render/figures/embedding.d.ts +43 -0
  70. package/dist/report-render/figures/embedding.js +360 -0
  71. package/dist/report-render/figures/equal-units.d.ts +101 -0
  72. package/dist/report-render/figures/equal-units.js +215 -0
  73. package/dist/report-render/figures/forest.d.ts +20 -0
  74. package/dist/report-render/figures/forest.js +398 -0
  75. package/dist/report-render/figures/gsea.d.ts +30 -0
  76. package/dist/report-render/figures/gsea.js +391 -0
  77. package/dist/report-render/figures/heatmap.d.ts +40 -0
  78. package/dist/report-render/figures/heatmap.js +395 -0
  79. package/dist/report-render/figures/index.d.ts +135 -0
  80. package/dist/report-render/figures/index.js +100 -0
  81. package/dist/report-render/figures/km.d.ts +16 -0
  82. package/dist/report-render/figures/km.js +406 -0
  83. package/dist/report-render/figures/label-room.d.ts +130 -0
  84. package/dist/report-render/figures/label-room.js +341 -0
  85. package/dist/report-render/figures/locuszoom.d.ts +50 -0
  86. package/dist/report-render/figures/locuszoom.js +675 -0
  87. package/dist/report-render/figures/lollipop.d.ts +16 -0
  88. package/dist/report-render/figures/lollipop.js +247 -0
  89. package/dist/report-render/figures/ma.d.ts +20 -0
  90. package/dist/report-render/figures/ma.js +114 -0
  91. package/dist/report-render/figures/manhattan.d.ts +60 -0
  92. package/dist/report-render/figures/manhattan.js +343 -0
  93. package/dist/report-render/figures/oncoprint.d.ts +41 -0
  94. package/dist/report-render/figures/oncoprint.js +420 -0
  95. package/dist/report-render/figures/pca.d.ts +37 -0
  96. package/dist/report-render/figures/pca.js +241 -0
  97. package/dist/report-render/figures/qq.d.ts +30 -0
  98. package/dist/report-render/figures/qq.js +182 -0
  99. package/dist/report-render/figures/roc.d.ts +17 -0
  100. package/dist/report-render/figures/roc.js +116 -0
  101. package/dist/report-render/figures/sankey.d.ts +48 -0
  102. package/dist/report-render/figures/sankey.js +353 -0
  103. package/dist/report-render/figures/upset.d.ts +60 -0
  104. package/dist/report-render/figures/upset.js +316 -0
  105. package/dist/report-render/figures/volcano.d.ts +25 -0
  106. package/dist/report-render/figures/volcano.js +166 -0
  107. package/dist/report-render/hybrid-svg.d.ts +75 -0
  108. package/dist/report-render/hybrid-svg.js +504 -0
  109. package/dist/report-render/number-format.d.ts +19 -0
  110. package/dist/report-render/number-format.js +25 -1
  111. package/dist/report-render/page.d.ts +41 -4
  112. package/dist/report-render/page.js +247 -12
  113. package/dist/report-render/render.d.ts +13 -4
  114. package/dist/report-render/render.js +37 -6
  115. package/dist/report-render/types.d.ts +23 -2
  116. package/dist/report-render/value-bridge.d.ts +38 -3
  117. package/dist/report-render/value-bridge.js +137 -6
  118. package/dist/report-render/views/chart-view.d.ts +42 -6
  119. package/dist/report-render/views/chart-view.js +70 -9
  120. package/dist/report-render/views/page-view.js +9 -10
  121. package/dist/runtime/assemble.js +2 -2
  122. package/dist/sandbox/client.d.ts +23 -32
  123. package/dist/sandbox/client.js +3 -11
  124. package/dist/sandbox/create-sandbox.d.ts +9 -24
  125. package/dist/sandbox/create-sandbox.js +15 -30
  126. package/dist/sandbox/docker-client.d.ts +12 -22
  127. package/dist/sandbox/docker-client.js +29 -43
  128. package/dist/sandbox/exec.d.ts +58 -0
  129. package/dist/sandbox/exec.js +197 -0
  130. package/dist/sandbox/identity.d.ts +5 -5
  131. package/dist/sandbox/identity.js +7 -10
  132. package/dist/sandbox/k8s-client.d.ts +4 -10
  133. package/dist/sandbox/k8s-client.js +5 -12
  134. package/dist/sandbox/liveness.d.ts +5 -12
  135. package/dist/sandbox/liveness.js +5 -12
  136. package/dist/sandbox/notification-sweep.d.ts +4 -7
  137. package/dist/sandbox/notification-sweep.js +4 -7
  138. package/dist/sandbox/reaper.d.ts +3 -3
  139. package/dist/sandbox/reaper.js +3 -3
  140. package/dist/sandbox/sandbox-error.d.ts +10 -11
  141. package/dist/sandbox/sandbox-error.js +10 -11
  142. package/dist/sandbox/sandbox-step-translate.d.ts +1 -1
  143. package/dist/sandbox/sandbox-step-translate.js +1 -1
  144. package/dist/sandbox/types.d.ts +31 -99
  145. package/dist/sandbox/types.js +17 -84
  146. package/dist/state/active-sandboxes.d.ts +7 -32
  147. package/dist/state/active-sandboxes.js +11 -46
  148. package/dist/state/index.d.ts +1 -2
  149. package/dist/state/index.js +1 -1
  150. package/dist/state/migrations/20261005120000_drop_the_active_exec_tracking.d.ts +7 -0
  151. package/dist/state/migrations/20261005120000_drop_the_active_exec_tracking.js +12 -0
  152. package/dist/state/migrations/index.js +2 -0
  153. package/dist/state/schema.d.ts +1 -5
  154. package/dist/state/schema.js +1 -6
  155. package/dist/state/step-executions.js +1 -2
  156. package/dist/tasks/data-profile.js +7 -20
  157. package/dist/tasks/derive-table-exec.d.ts +4 -14
  158. package/dist/tasks/derive-table-exec.js +9 -24
  159. package/dist/tasks/extract-values.d.ts +2 -2
  160. package/dist/tasks/extract-values.js +4 -7
  161. package/dist/tools/define-tool.d.ts +56 -13
  162. package/dist/tools/define-tool.js +28 -1
  163. package/dist/tools/execute-analysis.js +3 -3
  164. package/dist/tools/report-session/derive-table.d.ts +8 -9
  165. package/dist/tools/report-session/derive-table.js +3 -4
  166. package/dist/tools/report-session/preview-report.js +4 -49
  167. package/dist/tools/sandbox/list-available-packages.d.ts +1 -1
  168. package/dist/tools/sandbox/list-available-packages.js +5 -4
  169. package/dist/tools/workspace/edit-file.d.ts +5 -4
  170. package/dist/tools/workspace/edit-file.js +10 -13
  171. package/dist/tools/workspace/execute-command.d.ts +27 -42
  172. package/dist/tools/workspace/execute-command.js +42 -38
  173. package/dist/tools/workspace/index.d.ts +5 -6
  174. package/dist/tools/workspace/index.js +5 -6
  175. package/dist/tools/workspace/mutator.d.ts +43 -8
  176. package/dist/tools/workspace/mutator.js +45 -34
  177. package/dist/tools/workspace/run-exec.d.ts +3 -12
  178. package/dist/tools/workspace/run-exec.js +7 -24
  179. package/dist/tools/workspace/scan-inputs.d.ts +0 -3
  180. package/dist/tools/workspace/scan-inputs.js +0 -1
  181. package/dist/tools/workspace/write-file.d.ts +12 -3
  182. package/dist/tools/workspace/write-file.js +17 -13
  183. package/dist/workflows/sandbox-step.d.ts +7 -11
  184. package/dist/workflows/sandbox-step.js +24 -27
  185. package/package.json +3 -1
  186. package/dist/sandbox/await-exec.d.ts +0 -143
  187. package/dist/sandbox/await-exec.js +0 -417
  188. package/dist/sandbox/deliver-exec-event.d.ts +0 -25
  189. package/dist/sandbox/deliver-exec-event.js +0 -29
  190. package/dist/sandbox/digest.d.ts +0 -14
  191. package/dist/sandbox/digest.js +0 -17
  192. package/dist/sandbox/exec-id.d.ts +0 -12
  193. package/dist/sandbox/exec-id.js +0 -20
  194. package/dist/sandbox/hmac.d.ts +0 -62
  195. package/dist/sandbox/hmac.js +0 -72
  196. package/dist/sandbox/submit-exec.d.ts +0 -38
  197. package/dist/sandbox/submit-exec.js +0 -80
  198. package/dist/sandbox/watchdog.d.ts +0 -71
  199. package/dist/sandbox/watchdog.js +0 -161
@@ -7,7 +7,10 @@
7
7
  */
8
8
  import { AG_GRID_ASSET, assetSource, ECHARTS_ASSET, TSPROV_ASSET } from "./assets.js";
9
9
  import { CHART_SOURCE_MEMBER, POINT_LABEL } from "./chart.js";
10
- import { ECHARTS_THEME_NAME, GRID_HEADER_BORDER_PX, GRID_HEADER_HEIGHT_PX, GRID_MIN_COLUMN_WIDTH_PX, GRID_PRINT_ROW_CAP, GRID_ROW_HEIGHT_PX, GRID_THEME_PARAMS, GRID_TOOLTIP_DELAY_MS, GRID_VISIBLE_ROWS, } from "./design.js";
10
+ import { CHART_RENDERERS_SOURCE } from "./chart-renderers.js";
11
+ import { CHART_TOOLBOX_SOURCE, TOOLBOX_ZLEVEL } from "./chart-toolbox.js";
12
+ import { CHART_EXPORT_SIZES, ECHARTS_THEME_NAME, GRID_HEADER_BORDER_PX, GRID_HEADER_HEIGHT_PX, GRID_MIN_COLUMN_WIDTH_PX, GRID_PRINT_ROW_CAP, GRID_ROW_HEIGHT_PX, GRID_THEME_PARAMS, GRID_TOOLTIP_DELAY_MS, GRID_VISIBLE_ROWS, } from "./design.js";
13
+ import { HYBRID_SVG_SOURCE } from "./hybrid-svg.js";
11
14
  import { REPORT_PROVENANCE_GLOBAL } from "./provenance-data.js";
12
15
  import { scriptJson } from "./script-json.js";
13
16
  import { TABLE_DATA_GLOBAL } from "./table-data.js";
@@ -90,8 +93,9 @@ const SPY_BOTTOM_MARGIN_PERCENT = 70;
90
93
  *
91
94
  * The fragment writes its own constants, because a page script reads no module binding. Each rule here is
92
95
  * the twin of one rule of the server derivation: the number read of a cell, the four transforms, the
93
- * competition rank, the compare of a sort, and the three-way classification of a preset. A shared test
94
- * vector runs the transforms of both sides over one set of cells, thus the two cannot drift in silence.
96
+ * competition rank, the compare of a sort, the classification of a preset, and the item that adds
97
+ * the member of a continuous color and of a size after the pair. A shared test vector runs the transforms
98
+ * and the items of both sides over one set of cells, thus the two cannot drift in silence.
95
99
  *
96
100
  * The build reads the decoded rows of the payload. The decoder runs before this script, thus a row is a
97
101
  * record and a cell reads by its column name.
@@ -174,7 +178,17 @@ function reportColumn(payload, index) {
174
178
  }
175
179
  function reportChannel(payload, spec) {
176
180
  var cells = reportColumn(payload, spec.column);
177
- return spec.transform === undefined ? cells : reportTransform(cells, spec.transform);
181
+ if (spec.transform !== undefined) {
182
+ return reportTransform(cells, spec.transform);
183
+ }
184
+ if (spec.numeric !== true) {
185
+ return cells;
186
+ }
187
+ var numbers = [];
188
+ for (var c = 0; c < cells.length; c++) {
189
+ numbers.push(cells[c] === null ? null : reportNumber(cells[c]));
190
+ }
191
+ return numbers;
178
192
  }
179
193
  function reportCompare(a, b) {
180
194
  var left = reportNumber(a);
@@ -189,7 +203,7 @@ function reportCompare(a, b) {
189
203
  }
190
204
  return x > y ? 1 : 0;
191
205
  }
192
- function reportCategory(rule, xValue, yValue) {
206
+ function reportCategory(rule, xValue, yValue, pValue) {
193
207
  // The rule gives the place of the category, and the descriptor of a series names the same place. Thus
194
208
  // the two sides compare numbers and no category name rides the page.
195
209
  var x = reportNumber(xValue);
@@ -197,6 +211,13 @@ function reportCategory(rule, xValue, yValue) {
197
211
  if (!rule || x === null || y === null) {
198
212
  return -1;
199
213
  }
214
+ if (rule.kind === "ma") {
215
+ if (x <= 0) {
216
+ return -1;
217
+ }
218
+ var p = reportNumber(pValue);
219
+ return p !== null && p < rule.cut ? 0 : 1;
220
+ }
200
221
  if (y <= rule.cut) {
201
222
  return 2;
202
223
  }
@@ -208,8 +229,11 @@ function reportCategory(rule, xValue, yValue) {
208
229
  function reportSeriesData(payload, source, rule) {
209
230
  var x = reportChannel(payload, source.x);
210
231
  var y = reportChannel(payload, source.y);
232
+ var color = source.color === undefined ? null : reportChannel(payload, source.color);
233
+ var size = source.size === undefined ? null : reportChannel(payload, source.size);
211
234
  var group = source.group === undefined ? null : reportChannel(payload, source.group);
212
235
  var labels = source.label === undefined ? null : reportColumn(payload, source.label);
236
+ var ruleCells = rule && rule.kind === "ma" ? reportColumn(payload, payload.columns.indexOf(rule.column)) : null;
213
237
  var flags = Object.create(null);
214
238
  var declared = source.flags || [];
215
239
  for (var f = 0; f < declared.length; f++) {
@@ -223,20 +247,35 @@ function reportSeriesData(payload, source, rule) {
223
247
  if (group !== null && group[r] !== source.value) {
224
248
  continue;
225
249
  }
226
- if (source.category !== undefined && reportCategory(rule, x[r], y[r]) !== source.category) {
250
+ if (source.category !== undefined && reportCategory(rule, x[r], y[r], ruleCells === null ? null : ruleCells[r]) !== source.category) {
251
+ continue;
252
+ }
253
+ var shade = color === null ? null : reportNumber(color[r]);
254
+ var weight = size === null ? null : reportNumber(size[r]);
255
+ if ((color !== null && shade === null) || (size !== null && weight === null)) {
227
256
  continue;
228
257
  }
229
- points.push({ index: r, x: x[r], y: y[r] });
258
+ points.push({ index: r, x: x[r], y: y[r], color: shade, size: weight });
230
259
  }
231
260
  if (source.sort) {
232
261
  points.sort(function (a, b) {
233
262
  return reportCompare(a.x, b.x);
234
263
  });
264
+ } else if (source.rise) {
265
+ points.sort(function (a, b) {
266
+ return a.color - b.color;
267
+ });
235
268
  }
236
269
  var data = [];
237
270
  for (var p = 0; p < points.length; p++) {
238
271
  var point = points[p];
239
272
  var pair = source.swap ? [point.y, point.x] : [point.x, point.y];
273
+ if (point.color !== null) {
274
+ pair.push(point.color);
275
+ }
276
+ if (point.size !== null) {
277
+ pair.push(point.size);
278
+ }
240
279
  var label = labels === null ? null : labels[point.index];
241
280
  var named = label !== null && label !== undefined;
242
281
  if (!named && flags[point.index] !== true) {
@@ -251,17 +290,73 @@ function reportSeriesData(payload, source, rule) {
251
290
  }
252
291
  return data;
253
292
  }`;
293
+ /**
294
+ * The window global that holds the option of each chart, keyed by the id of its container.
295
+ *
296
+ * The option is plain data: its rows are built, and each renderer is still a name. The export of a chart
297
+ * draws it again from this entry, thus the page and each exported file read one option.
298
+ */
299
+ export const CHART_OPTIONS_GLOBAL = "__REPORT_CHART_OPTIONS";
300
+ /**
301
+ * The export sizes that the page draws, keyed by the name of the menu entry: the theme, the text size, the CSS
302
+ * box, the millimeter box of a column, and the pixel ratio of each export.
303
+ */
304
+ const PAGE_EXPORT_SIZES = Object.fromEntries(Object.entries(CHART_EXPORT_SIZES).map(([kind, size]) => [
305
+ kind,
306
+ {
307
+ theme: size.theme,
308
+ textPx: size.textPx,
309
+ width: size.widthPx,
310
+ height: size.heightPx,
311
+ ...("widthMm" in size ? { widthMm: size.widthMm, heightMm: size.heightMm } : {}),
312
+ pixelRatio: size.pixelRatio,
313
+ },
314
+ ]));
315
+ /**
316
+ * The time that the object URL of a built SVG file stays alive after its download starts, in milliseconds. The
317
+ * browser reads the URL when the download starts, and the page then frees the text.
318
+ */
319
+ const DOWNLOAD_URL_LIFETIME_MS = 60_000;
254
320
  /**
255
321
  * The page-side script that wires each chart. It finds every chart container, reads the option JSON from
256
322
  * the sibling `<script type="application/json">` element, and initializes ECharts with the registered
257
323
  * theme. The skeleton registers the theme before this script runs. A resize handler keeps each chart
258
- * fit to the window.
324
+ * fit to the window. It draws a chart again only when the container of the chart changes size. A capture past
325
+ * the viewport resizes the window and keeps each container, and a redraw there restarts the chunked render of
326
+ * a dense chart. A container of zero width or height keeps its chart as it is: ECharts 6.1.0 throws when a
327
+ * Sankey draws at a zero size (apache/echarts#21706), and a capture can give a container a zero width for one
328
+ * resize.
329
+ *
330
+ * A custom series names its renderer as a string, because the option is JSON. The script registers each named
331
+ * renderer with the chart runtime before the first chart initializes, and it keeps the option under the
332
+ * container id.
333
+ *
334
+ * The option carries the toolbox of the chart. Each handler of the toolbox is the name of a page function, and
335
+ * the script binds each function after the parse and before the chart initializes. The formatter of a genome axis
336
+ * is such a name too. After the init the script attaches the zoom handler of the toolbox to the chart. The
337
+ * download control opens the download menu of the card. The print hides the canvas layer of the toolbox of each
338
+ * chart, because the runtime draws the toolbox inside the chart body, and the end of the print shows the layer
339
+ * again. The print sets no option, because a new option starts the chunked draw of a dense chart again.
340
+ *
341
+ * A click on a PNG entry of the menu draws the kept option again on a detached element, at the CSS size of the
342
+ * export and its pixel ratio, in the theme of its text size. The entry states the height of its export, because
343
+ * a taller chart body grows the height of a column export. Each series draws in one pass, because the script
344
+ * reads the PNG in the same task and a chunked draw ends in a later frame. Then the script reads the PNG and
345
+ * downloads it through an anchor. An entry that names no kept chart, or no export size, draws nothing.
346
+ *
347
+ * A click on a hybrid SVG entry builds the SVG of a dense chart from the kept option: the vector draw, the raster
348
+ * draw of the point layer at 300 DPI, and the composition. Then the script downloads the text through an anchor.
349
+ * A build that gives no file keeps the menu open and shows its fault note, thus the reader sees why the entry gave
350
+ * no file and can take a PNG.
259
351
  *
260
352
  * An option that carries the data-source member holds no row. The script then reads the registered payload
261
353
  * of the artifact and builds the data of each series from the descriptors. A mount whose payload the
262
354
  * registry does not hold keeps its empty card, and the walk continues.
263
355
  *
264
- * The script signals readiness when the bootstrap completes, and immediately when no chart exists. It sets
356
+ * The script signals readiness when each chart that it set fires its first `finished` event, and immediately
357
+ * when no chart exists. A dense scatter draws in chunks over some frames, and the runtime fires `finished`
358
+ * after the last chunk. Thus a capture that keys on the signal sees each point. A chart whose option throws
359
+ * counts as finished, thus one fault never withholds the signal. It sets
265
360
  * the `window.__inflexaThemeReady` sentinel and dispatches the `inflexa-theme-ready` event on the document.
266
361
  * A reader that captures the page keys on this signal, thus the capture returns when the page is ready and
267
362
  * not at a timeout. The sentinel guards a listener that registers after the dispatch, thus a late listener
@@ -275,6 +370,9 @@ function reportSeriesData(payload, source, rule) {
275
370
  */
276
371
  export const CHART_BOOTSTRAP = `(function () {
277
372
  ${CHART_SERIES_BUILDER}
373
+ ${CHART_RENDERERS_SOURCE}
374
+ ${HYBRID_SVG_SOURCE}
375
+ ${CHART_TOOLBOX_SOURCE}
278
376
  function signalReady() {
279
377
  window.${THEME_READY_SENTINEL} = true;
280
378
  document.dispatchEvent(new Event(${JSON.stringify(THEME_READY_EVENT)}));
@@ -291,7 +389,31 @@ export const CHART_BOOTSTRAP = `(function () {
291
389
  whenRevealed(signalReady);
292
390
  return;
293
391
  }
392
+ reportRegisterRenderers(echarts);
393
+ var pending = 0;
394
+ var walked = false;
395
+ var signalled = false;
396
+ function settle() {
397
+ if (!signalled && walked && pending === 0) {
398
+ signalled = true;
399
+ whenRevealed(signalReady);
400
+ }
401
+ }
402
+ function track(chart) {
403
+ var done = false;
404
+ pending += 1;
405
+ function finish() {
406
+ if (!done) {
407
+ done = true;
408
+ pending -= 1;
409
+ settle();
410
+ }
411
+ }
412
+ chart.on("finished", finish);
413
+ return finish;
414
+ }
294
415
  var registry = window.${TABLE_DATA_GLOBAL};
416
+ var kept = window.${CHART_OPTIONS_GLOBAL} = window.${CHART_OPTIONS_GLOBAL} || Object.create(null);
295
417
  var containers = document.querySelectorAll("[data-echarts-id]");
296
418
  for (var i = 0; i < containers.length; i++) {
297
419
  var container = containers[i];
@@ -314,8 +436,17 @@ export const CHART_BOOTSTRAP = `(function () {
314
436
  series[s].data = reportSeriesData(payload, source.series[s], source.rule);
315
437
  }
316
438
  }
439
+ reportBindToolbox(option);
317
440
  var chart = echarts.init(container, ${JSON.stringify(ECHARTS_THEME_NAME)});
318
- chart.setOption(option);
441
+ reportWatchZoom(chart, option);
442
+ kept[container.getAttribute("id") || ""] = option;
443
+ var finish = track(chart);
444
+ try {
445
+ chart.setOption(option);
446
+ } catch (fault) {
447
+ finish();
448
+ throw fault;
449
+ }
319
450
  } catch (cause) {
320
451
  console.error("chart bootstrap failed for " + (container.getAttribute("data-echarts-id") || "(no id)") + ": " + (cause && cause.message ? cause.message : cause));
321
452
  }
@@ -324,12 +455,116 @@ export const CHART_BOOTSTRAP = `(function () {
324
455
  var nodes = document.querySelectorAll("[data-echarts-id]");
325
456
  for (var j = 0; j < nodes.length; j++) {
326
457
  var instance = echarts.getInstanceByDom(nodes[j]);
327
- if (instance) {
458
+ var width = nodes[j].clientWidth;
459
+ var height = nodes[j].clientHeight;
460
+ if (instance && width > 0 && height > 0 && (instance.getWidth() !== width || instance.getHeight() !== height)) {
328
461
  instance.resize();
329
462
  }
330
463
  }
331
464
  });
332
- whenRevealed(signalReady);
465
+ function toolboxShown(shown) {
466
+ function place(layer) {
467
+ if (layer.zlevel === ${TOOLBOX_ZLEVEL} && layer.dom) {
468
+ layer.dom.style.visibility = shown ? "" : "hidden";
469
+ }
470
+ }
471
+ var nodes = document.querySelectorAll("[data-echarts-id]");
472
+ for (var n = 0; n < nodes.length; n++) {
473
+ var instance = echarts.getInstanceByDom(nodes[n]);
474
+ var painter = instance ? instance.getZr().painter : null;
475
+ if (painter && typeof painter.eachLayer === "function") {
476
+ painter.eachLayer(place);
477
+ }
478
+ }
479
+ }
480
+ window.addEventListener("beforeprint", function () {
481
+ reportCloseExportMenu(false);
482
+ toolboxShown(false);
483
+ });
484
+ window.addEventListener("afterprint", function () {
485
+ toolboxShown(true);
486
+ });
487
+ var exportSizes = ${JSON.stringify(PAGE_EXPORT_SIZES)};
488
+ function reportOnePassOption(option) {
489
+ var out = Object.assign({}, option);
490
+ var series = reportList(option.series);
491
+ var drawn = [];
492
+ for (var s = 0; s < series.length; s++) {
493
+ drawn.push(Object.assign({}, series[s], { progressive: 0 }));
494
+ }
495
+ out.series = drawn;
496
+ return out;
497
+ }
498
+ function reportExportPng(control) {
499
+ var kind = control.getAttribute("data-export") || "";
500
+ var chartId = control.getAttribute("data-chart") || "";
501
+ if (!Object.prototype.hasOwnProperty.call(exportSizes, kind) || !Object.prototype.hasOwnProperty.call(kept, chartId)) {
502
+ return;
503
+ }
504
+ var size = exportSizes[kind];
505
+ var height = Number(control.getAttribute("data-height")) || size.height;
506
+ var host = document.createElement("div");
507
+ var offscreen = echarts.init(host, size.theme, { renderer: "canvas", width: size.width, height: height, devicePixelRatio: size.pixelRatio });
508
+ try {
509
+ offscreen.setOption(reportOnePassOption(reportExportOption(kept[chartId], size.textPx, size.width, height)));
510
+ var link = document.createElement("a");
511
+ link.href = offscreen.getDataURL({ type: "png", pixelRatio: size.pixelRatio, backgroundColor: "#ffffff" });
512
+ link.download = control.getAttribute("data-file") || "chart.png";
513
+ document.body.appendChild(link);
514
+ link.click();
515
+ document.body.removeChild(link);
516
+ } finally {
517
+ offscreen.dispose();
518
+ }
519
+ }
520
+ function reportExportHybrid(control) {
521
+ var kind = control.getAttribute("data-hybrid") || "";
522
+ var chartId = control.getAttribute("data-chart") || "";
523
+ if (!Object.prototype.hasOwnProperty.call(exportSizes, kind) || !Object.prototype.hasOwnProperty.call(kept, chartId)) {
524
+ return true;
525
+ }
526
+ var size = Object.assign({}, exportSizes[kind]);
527
+ size.height = Number(control.getAttribute("data-height")) || size.height;
528
+ size.heightMm = Number(control.getAttribute("data-height-mm")) || size.heightMm;
529
+ var svg = null;
530
+ try {
531
+ svg = reportHybridSvg(echarts, kept[chartId], size);
532
+ } catch (cause) {
533
+ console.error("hybrid svg failed for " + chartId + ": " + (cause && cause.message ? cause.message : cause));
534
+ return false;
535
+ }
536
+ if (svg === null) {
537
+ console.error("hybrid svg failed for " + chartId + ": the chart holds no point layer inside a grid");
538
+ return false;
539
+ }
540
+ var url = URL.createObjectURL(new Blob([svg], { type: "image/svg+xml" }));
541
+ var link = document.createElement("a");
542
+ link.href = url;
543
+ link.download = control.getAttribute("data-file") || "chart.svg";
544
+ document.body.appendChild(link);
545
+ link.click();
546
+ document.body.removeChild(link);
547
+ setTimeout(function () {
548
+ URL.revokeObjectURL(url);
549
+ }, ${DOWNLOAD_URL_LIFETIME_MS});
550
+ return true;
551
+ }
552
+ document.addEventListener("click", function (event) {
553
+ var target = event.target;
554
+ var control = target && typeof target.closest === "function" ? target.closest("[data-export], [data-hybrid]") : null;
555
+ if (control) {
556
+ if (control.getAttribute("data-export") !== null) {
557
+ reportExportPng(control);
558
+ } else if (reportExportHybrid(control) === false) {
559
+ reportMenuFault(control);
560
+ return;
561
+ }
562
+ }
563
+ reportMenuClick(event);
564
+ });
565
+ document.addEventListener("keydown", reportMenuKey);
566
+ walked = true;
567
+ settle();
333
568
  })();`;
334
569
  /**
335
570
  * The page-side script that reveals each `fade-in` element as it enters the viewport. The `data-delay`
@@ -17,20 +17,23 @@
17
17
  import { type Result } from "neverthrow";
18
18
  import type { ReportDocument } from "../contracts/report-blocks.js";
19
19
  import { type CitationRecords } from "../report-model/reference-resolver.js";
20
+ import { type ChartOpts } from "./chart.js";
20
21
  import { type DerivationChain } from "./references.js";
21
22
  import { type ProvenanceExport } from "./provenance-data.js";
22
23
  import type { RenderedPage, RenderProblem, RenderValues } from "./types.js";
23
24
  /**
24
- * The optional inputs of one render: the bibliography of the pin, the chains of the session, and the frozen
25
- * provenance of the analysis.
25
+ * The optional inputs of one render: the bibliography of the pin, the chains of the session, the frozen
26
+ * provenance of the analysis, and the options of the chart derivation.
26
27
  *
27
28
  * Each member is absent on its own, and a caller gives the members that it holds. A positional tail of
28
- * three optional inputs made a caller of the last one pad the two before it, thus the three ride one bag.
29
+ * three optional inputs made a caller of the last one pad the two before it, thus the inputs ride one bag.
30
+ * An absent `chart` member derives each chart with the registered figure modules.
29
31
  */
30
32
  export interface RenderOptions {
31
33
  readonly records?: CitationRecords;
32
34
  readonly derivations?: readonly DerivationChain[];
33
35
  readonly provenance?: ProvenanceExport;
36
+ readonly chart?: ChartOpts;
34
37
  }
35
38
  /**
36
39
  * Render a report document, its value map, and the pinned citation records to one page and its data assets.
@@ -56,7 +59,13 @@ export interface RenderOptions {
56
59
  * The renderer writes no file. The data assets ride the result, and the caller stages them beside the page.
57
60
  * Thus the render stays pure, and two renders of one document give byte-identical assets.
58
61
  *
62
+ * Each chart gives two SVG files beside the data assets, and its card links them. The page loads each table
63
+ * payload as a script, and it loads no SVG. Thus the SVG files ride the staged list of the result alone.
64
+ *
59
65
  * One artifact gives one payload. A table block and a chart block that bind it read that one asset, and a
60
66
  * chart under the inline bound carries its rows in its own option and registers none.
67
+ *
68
+ * The render is asynchronous because it loads the chart runtime of the SVG export. Each other step is
69
+ * synchronous.
61
70
  */
62
- export declare function renderReportPage(document: ReportDocument, values: RenderValues, { records, derivations, provenance }?: RenderOptions): Result<RenderedPage, RenderProblem[]>;
71
+ export declare function renderReportPage(document: ReportDocument, values: RenderValues, { records, derivations, provenance, chart }?: RenderOptions): Promise<Result<RenderedPage, RenderProblem[]>>;
@@ -18,7 +18,8 @@ import { err, ok } from "neverthrow";
18
18
  import { serializeReference } from "../contracts/report-reference.js";
19
19
  import { citationRecordOf } from "../report-model/reference-resolver.js";
20
20
  import { renderChart } from "./views/chart-view.js";
21
- import { deriveChartRender } from "./chart.js";
21
+ import { DEFAULT_CHART_OPTS, deriveChartRender } from "./chart.js";
22
+ import { chartSvgAssets, loadChartRuntime } from "./chart-export.js";
22
23
  import { assemblePage, renderBand, renderReferenceSection } from "./views/page-view.js";
23
24
  import { renderClaim, renderNav, renderSection, renderText } from "./views/prose.js";
24
25
  import { citationKeyOf, derivationChains, ReferenceLedger } from "./references.js";
@@ -49,13 +50,26 @@ import { renderCitation, renderFigure, renderMetric, renderMetricGrid, renderTab
49
50
  * The renderer writes no file. The data assets ride the result, and the caller stages them beside the page.
50
51
  * Thus the render stays pure, and two renders of one document give byte-identical assets.
51
52
  *
53
+ * Each chart gives two SVG files beside the data assets, and its card links them. The page loads each table
54
+ * payload as a script, and it loads no SVG. Thus the SVG files ride the staged list of the result alone.
55
+ *
52
56
  * One artifact gives one payload. A table block and a chart block that bind it read that one asset, and a
53
57
  * chart under the inline bound carries its rows in its own option and registers none.
58
+ *
59
+ * The render is asynchronous because it loads the chart runtime of the SVG export. Each other step is
60
+ * synchronous.
54
61
  */
55
- export function renderReportPage(document, values, { records, derivations, provenance } = {}) {
62
+ export async function renderReportPage(document, values, { records, derivations, provenance, chart = DEFAULT_CHART_OPTS } = {}) {
56
63
  const problems = [];
57
64
  const ledger = new ReferenceLedger();
58
- const data = { payloads: new Map(), mounts: 0, view: { lineage: provenance !== undefined } };
65
+ const data = {
66
+ payloads: new Map(),
67
+ mounts: 0,
68
+ svgs: [],
69
+ view: { lineage: provenance !== undefined },
70
+ chart,
71
+ chartRuntime: await loadChartRuntime(),
72
+ };
59
73
  const content = [];
60
74
  for (const [index, section] of document.sections.entries()) {
61
75
  content.push(renderBand(index, renderBlock(section, values, ledger, records, data, 0, problems)));
@@ -71,7 +85,7 @@ export function renderReportPage(document, values, { records, derivations, prove
71
85
  const references = renderReferenceSection(ledger, document.sections.length, records, derivationChains(derivations));
72
86
  return ok({
73
87
  html: assemblePage(document.title, nav, content.join(""), references, { dataAssets: tableAssets, grids: data.mounts > 0, provenanceAssets }),
74
- dataAssets: [...provenanceAssets, ...tableAssets],
88
+ dataAssets: [...provenanceAssets, ...tableAssets, ...data.svgs],
75
89
  });
76
90
  }
77
91
  /**
@@ -159,7 +173,11 @@ function renderBlock(block, values, ledger, records, data, depth, problems) {
159
173
  // under two ids. One binding resolves one row set, thus the column places and the row places of
160
174
  // the descriptors address the registered payload whichever block encoded it.
161
175
  const columns = tableColumns(entry);
162
- const derived = deriveChartRender(block, entry.rows, entry.columns, { key: block.id, columns });
176
+ const derived = deriveChartRender(block, entry.rows, entry.columns, { key: block.id, columns }, {
177
+ ...(entry.statistics !== undefined ? { statistics: entry.statistics } : {}),
178
+ ...(entry.track !== undefined ? { track: entry.track } : {}),
179
+ ...(entry.trees !== undefined ? { trees: entry.trees } : {}),
180
+ }, data.chart);
163
181
  if (derived.isErr()) {
164
182
  problems.push(derived.error);
165
183
  return "";
@@ -167,7 +185,20 @@ function renderBlock(block, values, ledger, records, data, depth, problems) {
167
185
  if (derived.value.readsPayload) {
168
186
  registerPayload(data, block.binding, block.id, () => payloadOf(block.binding, entry, columns));
169
187
  }
170
- return renderChart(block, ledger, derived.value.option, data.view);
188
+ // The export reads the chart with every row inline, thus a dense chart exports each point.
189
+ const svgs = chartSvgAssets(data.chartRuntime, block.id, derived.value.inline, derived.value.bodyPx);
190
+ if (svgs.isErr()) {
191
+ problems.push(svgs.error);
192
+ return "";
193
+ }
194
+ if (svgs.value !== undefined) {
195
+ data.svgs.push(svgs.value.single, svgs.value.double);
196
+ }
197
+ return renderChart(block, ledger, derived.value.option, data.view, {
198
+ bodyPx: derived.value.bodyPx,
199
+ ...(derived.value.widthPx !== undefined ? { widthPx: derived.value.widthPx } : {}),
200
+ ...(svgs.value !== undefined ? { svg: { single: svgs.value.single.name, double: svgs.value.double.name } } : {}),
201
+ });
171
202
  }
172
203
  case "section":
173
204
  return renderSection(block, depth, renderChildren(block.blocks, values, ledger, records, data, depth + 1, problems));
@@ -8,6 +8,21 @@
8
8
  * image bytes.
9
9
  */
10
10
  import type { DataAsset } from "./table-data.js";
11
+ /** One statistic of a chart: the label of the block, and the value of the cell that the statistic binds. */
12
+ export interface RenderStatistic {
13
+ label: string;
14
+ value: string | number;
15
+ }
16
+ /** The second table of a chart: the rows of the track binding, and the optional column order. */
17
+ export interface RenderTrack {
18
+ rows: Array<Record<string, string | number>>;
19
+ columns?: string[];
20
+ }
21
+ /** The trees of the category axes of a chart: the rows of the tree binding of each axis, with its optional column order. */
22
+ export interface RenderTrees {
23
+ x?: RenderTrack;
24
+ y?: RenderTrack;
25
+ }
11
26
  /**
12
27
  * The value that one block resolves to, as a closed union. A `scalar` gives one number or one string. A
13
28
  * `table` gives the rows and the optional column order. A `figure` gives a ready source string. A
@@ -15,6 +30,9 @@ import type { DataAsset } from "./table-data.js";
15
30
  *
16
31
  * A `table` carries `total` when a row bound cut the rows of the artifact. The renderer states the shown
17
32
  * count against it. A table with no bound carries none, and the row count answers for it.
33
+ *
34
+ * The `table` of a chart also carries the value of each statistic, in block order, the track, and the tree of
35
+ * each axis, where the block declares them. A table block declares none of them.
18
36
  */
19
37
  export type RenderValue = {
20
38
  type: "scalar";
@@ -24,6 +42,9 @@ export type RenderValue = {
24
42
  rows: Array<Record<string, string | number>>;
25
43
  columns?: string[];
26
44
  total?: number;
45
+ statistics?: RenderStatistic[];
46
+ track?: RenderTrack;
47
+ trees?: RenderTrees;
27
48
  } | {
28
49
  type: "figure";
29
50
  src: string;
@@ -48,8 +69,8 @@ export type RenderValues = Record<string, RenderValue>;
48
69
  * The renderer writes no file. Thus it gives the asset bytes back to the caller, and the caller stages
49
70
  * them beside the page. A document with no table gives an empty list, and its page references none.
50
71
  *
51
- * The list holds the provenance assets of the render beside the table payloads, thus one stage writes each
52
- * file that the page loads and the caller sorts nothing.
72
+ * The list holds the provenance assets of the render beside the table payloads and the SVG files of each
73
+ * chart, thus one stage writes each file that the page loads or links, and the caller sorts nothing.
53
74
  */
54
75
  export interface RenderedPage {
55
76
  html: string;
@@ -13,6 +13,8 @@
13
13
  * one call reports each fault at one time.
14
14
  */
15
15
  import { type Result } from "neverthrow";
16
+ import type { Block } from "../contracts/report-blocks.js";
17
+ import { type ChartSlot } from "../report-model/block-walk.js";
16
18
  import type { ResolvedValue } from "../report-model/reference-resolver.js";
17
19
  import type { RenderValues } from "./types.js";
18
20
  /** The block kinds that carry no render value. Each one renders from the block and the ledger alone. */
@@ -25,28 +27,46 @@ export type ResolvedFile = Extract<ResolvedValue, {
25
27
  }>;
26
28
  /** The policy that turns a resolved file echo into a figure `src` string. The caller owns the policy. */
27
29
  export type FigureSourcePolicy = (file: ResolvedFile) => string;
30
+ /** One resolved statistic of a chart: the label of the block, and the resolved value of its cell. */
31
+ export interface StatisticResolution {
32
+ label: string;
33
+ resolved: ResolvedValue;
34
+ }
28
35
  /**
29
36
  * The resolution result of one block: the block id, the block kind, and the resolved value of its binding.
30
37
  *
31
38
  * A value-bearing kind carries the resolved value. A no-value kind carries none, thus a text block with
32
- * no binding is representable and a value on it is not.
39
+ * no binding is representable and a value on it is not. A chart also carries the resolved track, the resolved
40
+ * tree of each axis, and each resolved statistic, in block order, where the block declares them.
33
41
  */
34
42
  export type BlockResolution = {
35
43
  blockId: string;
36
44
  kind: NoValueKind;
37
45
  } | {
38
46
  blockId: string;
39
- kind: ValueBearingKind;
47
+ kind: Exclude<ValueBearingKind, "chart">;
48
+ resolved: ResolvedValue;
49
+ } | {
50
+ blockId: string;
51
+ kind: "chart";
40
52
  resolved: ResolvedValue;
53
+ track?: ResolvedValue;
54
+ trees?: {
55
+ x?: ResolvedValue;
56
+ y?: ResolvedValue;
57
+ };
58
+ statistics?: StatisticResolution[];
41
59
  };
42
60
  /**
43
61
  * One typed mismatch between a block kind and a resolved value type. `blockId` names the block, and
44
62
  * `blockKind` names the kind that sets the expectation. `expected` names the resolved type that the kind
45
- * needs, and `actual` names the resolved type that arrived.
63
+ * needs, and `actual` names the resolved type that arrived. `slot` names the track or the statistic of a
64
+ * chart whose value has the wrong type.
46
65
  */
47
66
  export interface BridgeMismatch {
48
67
  blockId: string;
49
68
  blockKind: ValueBearingKind;
69
+ slot?: ChartSlot;
50
70
  expected: ResolvedValue["type"];
51
71
  actual: ResolvedValue["type"];
52
72
  }
@@ -59,4 +79,19 @@ export interface BridgeMismatch {
59
79
  * complete map.
60
80
  */
61
81
  export declare function bridgeValues(resolutions: readonly BlockResolution[], figureSrc: FigureSourcePolicy): Result<RenderValues, BridgeMismatch[]>;
82
+ /**
83
+ * Pair each block with the resolved value of its binding, in document order.
84
+ *
85
+ * A value-bearing kind carries the resolved value of its binding, looked up by the block id from the map
86
+ * that the resolution pass filled. A chart also carries the value of its track, of each tree, and of each statistic, looked
87
+ * up by the block id and the slot. The label of a statistic comes from the block, because the render value
88
+ * prints it. A no-value kind carries none. Where a reference sits in a block is the knowledge of
89
+ * `block-walk.ts`, thus this walk reads the slots that the walk names and never a binding field.
90
+ *
91
+ * The switch is exhaustive over the eight block kinds. A ninth kind reaches the end with no return, and
92
+ * the declared return type fails the build. Thus the walk cannot drop a kind in silence. A value-bearing
93
+ * block reaches here only when its reference resolved, because an unresolved reference short-circuits
94
+ * before this walk.
95
+ */
96
+ export declare function collectResolutions(blocks: readonly Block[], resolvedByBlock: ReadonlyMap<string, ResolvedValue>, resolvedBySlot: ReadonlyMap<string, ReadonlyMap<ChartSlot, ResolvedValue>>): BlockResolution[];
62
97
  export {};