@ovalbee/reportifact 0.0.1
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/README.md +841 -0
- package/dist/Chart.vue_vue_type_style_index_0_lang-B4novMFC.cjs +1 -0
- package/dist/Chart.vue_vue_type_style_index_0_lang-BYILFTfl.js +5895 -0
- package/dist/ImageGallery.vue_vue_type_style_index_0_lang-BB4dG0CB.cjs +1 -0
- package/dist/ImageGallery.vue_vue_type_style_index_0_lang-DPY6mSdA.js +191 -0
- package/dist/MetricCallout.vue_vue_type_style_index_0_lang-BZbc-Wkf.js +49 -0
- package/dist/MetricCallout.vue_vue_type_style_index_0_lang-WpuALh8Z.cjs +1 -0
- package/dist/MetricCards.vue_vue_type_style_index_0_lang-CDPSu7no.cjs +1 -0
- package/dist/MetricCards.vue_vue_type_style_index_0_lang-CfxCCXmB.js +33 -0
- package/dist/MetricsTable.vue_vue_type_style_index_0_lang-BzrxoJW4.cjs +1 -0
- package/dist/MetricsTable.vue_vue_type_style_index_0_lang-CO9HvYfr.js +132 -0
- package/dist/Reportifact.vue.d.ts +40 -0
- package/dist/ReportifactWidgetHost-A73gQ3bT.cjs +1 -0
- package/dist/ReportifactWidgetHost-BjKBwCWI.js +17 -0
- package/dist/Timeline.vue_vue_type_style_index_0_lang-BIyC7N9b.cjs +2 -0
- package/dist/Timeline.vue_vue_type_style_index_0_lang-BnrgKBMh.js +2598 -0
- package/dist/WidgetWrapper.vue_vue_type_style_index_0_lang-BQQ4Vv5j.cjs +1 -0
- package/dist/WidgetWrapper.vue_vue_type_style_index_0_lang-D-oXqmI2.js +59 -0
- package/dist/components/TableOfContents.vue.d.ts +15 -0
- package/dist/composables/useDelayedLoading.d.ts +6 -0
- package/dist/composables/useFetch.d.ts +13 -0
- package/dist/customWidgets.d.ts +18 -0
- package/dist/entries/chart.d.ts +3 -0
- package/dist/entries/contracts.d.ts +3 -0
- package/dist/entries/figure.d.ts +1 -0
- package/dist/entries/host.d.ts +1 -0
- package/dist/entries/imageGallery.d.ts +3 -0
- package/dist/entries/metricCallout.d.ts +2 -0
- package/dist/entries/metricCards.d.ts +2 -0
- package/dist/entries/metricsTable.d.ts +2 -0
- package/dist/entries/timeline.d.ts +2 -0
- package/dist/index-DFz2A8yJ.js +21229 -0
- package/dist/index-DmTzvbys.cjs +47 -0
- package/dist/index.d.ts +6 -0
- package/dist/markdownTable-B5207r3E.js +32 -0
- package/dist/markdownTable-Cbn1Du3L.cjs +2 -0
- package/dist/mediaResolver.d.ts +18 -0
- package/dist/middleware.cjs +1 -0
- package/dist/middleware.d.ts +21 -0
- package/dist/middleware.js +177 -0
- package/dist/portable/ReportifactFigure.vue.d.ts +36 -0
- package/dist/portable/ReportifactWidgetHost.vue.d.ts +17 -0
- package/dist/portable/contracts.d.ts +54 -0
- package/dist/reportifact.cjs +67 -0
- package/dist/reportifact.d.ts +2 -0
- package/dist/reportifact.js +5272 -0
- package/dist/style.css +13 -0
- package/dist/useFetch-BfOAq-Kg.cjs +1 -0
- package/dist/useFetch-CHQm_TmB.js +35 -0
- package/dist/utils/createMarked.d.ts +7 -0
- package/dist/utils/parseIndexMd.d.ts +29 -0
- package/dist/utils/tfeventsParser.cjs +1 -0
- package/dist/utils/tfeventsParser.d.ts +21 -0
- package/dist/utils/tfeventsParser.js +336 -0
- package/dist/widgets/Card.vue.d.ts +6 -0
- package/dist/widgets/Chart.vue.d.ts +26 -0
- package/dist/widgets/HelpTooltip.vue.d.ts +5 -0
- package/dist/widgets/ImageGallery.vue.d.ts +26 -0
- package/dist/widgets/Markdown.vue.d.ts +8 -0
- package/dist/widgets/MetricCallout.vue.d.ts +19 -0
- package/dist/widgets/MetricCards.vue.d.ts +13 -0
- package/dist/widgets/MetricTable.vue.d.ts +6 -0
- package/dist/widgets/MetricsTable.vue.d.ts +17 -0
- package/dist/widgets/ModelClassComparison.vue.d.ts +7 -0
- package/dist/widgets/ModelComparisonGallery.vue.d.ts +15 -0
- package/dist/widgets/Notice.vue.d.ts +6 -0
- package/dist/widgets/ReportifactWidget.vue.d.ts +23 -0
- package/dist/widgets/Scope.vue.d.ts +8 -0
- package/dist/widgets/Table.vue.d.ts +18 -0
- package/dist/widgets/Tensorboard.vue.d.ts +13 -0
- package/dist/widgets/Timeline.vue.d.ts +21 -0
- package/dist/widgets/WidgetWrapper.vue.d.ts +28 -0
- package/dist/widgets/chart-config/barGradient.d.ts +2 -0
- package/dist/widgets/chart-config/barGrouped.d.ts +2 -0
- package/dist/widgets/chart-config/barNormalized.d.ts +2 -0
- package/dist/widgets/chart-config/barStacked.d.ts +2 -0
- package/dist/widgets/chart-config/customGauge.d.ts +2 -0
- package/dist/widgets/chart-config/gaugeGrade.d.ts +2 -0
- package/dist/widgets/chart-config/heatmap.d.ts +2 -0
- package/dist/widgets/chart-config/index.d.ts +4 -0
- package/dist/widgets/chart-config/line.d.ts +2 -0
- package/dist/widgets/chart-config/pie.d.ts +2 -0
- package/dist/widgets/chart-config/radar.d.ts +2 -0
- package/dist/widgets/chart-config/shared.d.ts +15 -0
- package/dist/widgets/chart-config/types.d.ts +96 -0
- package/dist/widgets/chart.cjs +1 -0
- package/dist/widgets/chart.d.ts +6 -0
- package/dist/widgets/chart.js +5 -0
- package/dist/widgets/chartSizing.d.ts +1 -0
- package/dist/widgets/contracts.cjs +1 -0
- package/dist/widgets/contracts.d.ts +2 -0
- package/dist/widgets/contracts.js +1 -0
- package/dist/widgets/figure.cjs +1 -0
- package/dist/widgets/figure.d.ts +6 -0
- package/dist/widgets/figure.js +84 -0
- package/dist/widgets/host.cjs +1 -0
- package/dist/widgets/host.d.ts +6 -0
- package/dist/widgets/host.js +5 -0
- package/dist/widgets/image-gallery.cjs +1 -0
- package/dist/widgets/image-gallery.d.ts +6 -0
- package/dist/widgets/image-gallery.js +5 -0
- package/dist/widgets/markdownTable.d.ts +4 -0
- package/dist/widgets/metric-callout.cjs +1 -0
- package/dist/widgets/metric-callout.d.ts +6 -0
- package/dist/widgets/metric-callout.js +5 -0
- package/dist/widgets/metric-cards.cjs +1 -0
- package/dist/widgets/metric-cards.d.ts +6 -0
- package/dist/widgets/metric-cards.js +5 -0
- package/dist/widgets/metrics-table.cjs +1 -0
- package/dist/widgets/metrics-table.d.ts +6 -0
- package/dist/widgets/metrics-table.js +5 -0
- package/dist/widgets/scope.d.ts +77 -0
- package/dist/widgets/timeline.cjs +1 -0
- package/dist/widgets/timeline.d.ts +6 -0
- package/dist/widgets/timeline.js +5 -0
- package/package.json +125 -0
package/README.md
ADDED
|
@@ -0,0 +1,841 @@
|
|
|
1
|
+
# Reportifact
|
|
2
|
+
|
|
3
|
+
Vue report renderer for report assets. A report is a directory with an `index.md`
|
|
4
|
+
plus optional JSON, Parquet, Markdown, TensorBoard event, image, or other files
|
|
5
|
+
referenced from that index.
|
|
6
|
+
|
|
7
|
+
## Widget Index
|
|
8
|
+
|
|
9
|
+
| Widget | Use For |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `grid` | Group widgets with semantic layouts like `auto`, `2`, `4-2`. |
|
|
12
|
+
| `card` | Bordered Markdown panel with optional title, subtitle, and badge. |
|
|
13
|
+
| `notice` | Lightweight placeholder, TODO, or informational callout. |
|
|
14
|
+
| `metricCards` | Compact metric tiles from a Markdown table. |
|
|
15
|
+
| `metricRows` | Metric rows with values, descriptions, and help tooltips. |
|
|
16
|
+
| `metricCallout` | One highlighted metric plus explanatory Markdown. |
|
|
17
|
+
| `imageGallery` | JSON-backed grid of images with optional overlay layers. |
|
|
18
|
+
| `modelComparisonGallery` | Model-comparison image cards that open per-item previews. |
|
|
19
|
+
| `chart` | JSON-backed ECharts visualizations. |
|
|
20
|
+
| `modelClassComparison` | Compact comparison of per-class metrics across ML models. |
|
|
21
|
+
| `table` | Parquet table with filtering, sorting, pagination, and row clicks. |
|
|
22
|
+
| `timeline` | JSON-backed per-row time tracks with point markers (e.g. videos with cut points). |
|
|
23
|
+
| `markdown` | Render a separate Markdown file. |
|
|
24
|
+
| `tensorboard` | TensorBoard scalar event charts. |
|
|
25
|
+
| `metricsTable` | JSON-backed metrics/table visualization. |
|
|
26
|
+
| `scope` | Sticky control to re-render the whole report for a data scope (asset-tag value). |
|
|
27
|
+
| `evaluationReports` | OvalBee-only custom widget for evaluation reports linked to a model asset. |
|
|
28
|
+
| `modelCheckpoints` | OvalBee-only custom widget for model checkpoint downloads. |
|
|
29
|
+
|
|
30
|
+
Built-in widgets are provided by Reportifact. OvalBee-only widgets must be
|
|
31
|
+
registered by the OvalBee host through `customWidgets`.
|
|
32
|
+
|
|
33
|
+
## OvalBee Custom Widgets
|
|
34
|
+
|
|
35
|
+
| Widget | Use For | Host Component |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| `evaluationReports` | Evaluation report links for a model asset. | `web/src/components/reportifact/custom-widgets/EvaluationReportsWidget.vue` |
|
|
38
|
+
| `modelCheckpoints` | Download links for checkpoint resources on a model asset. | `web/src/components/reportifact/custom-widgets/ModelCheckpointsWidget.vue` |
|
|
39
|
+
|
|
40
|
+
## `index.md` Shape
|
|
41
|
+
|
|
42
|
+
Top-level `# H1` headings become report sections and table-of-contents tabs.
|
|
43
|
+
Inside a section, use Markdown prose and `::widget` directives.
|
|
44
|
+
|
|
45
|
+
```md
|
|
46
|
+
# Overview
|
|
47
|
+
|
|
48
|
+
Plain Markdown text.
|
|
49
|
+
|
|
50
|
+
::chart
|
|
51
|
+
src: metrics.json
|
|
52
|
+
type: radar
|
|
53
|
+
title: Metrics
|
|
54
|
+
::
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Do not put raw HTML, inline CSS, or `<style>` blocks in report Markdown. Add a
|
|
58
|
+
Reportifact widget instead. Markdown is supported inside widgets that accept a
|
|
59
|
+
body, such as `card`, `notice`, `metricRows`, and `metricCards`.
|
|
60
|
+
|
|
61
|
+
## Directive Syntax
|
|
62
|
+
|
|
63
|
+
Two forms are supported:
|
|
64
|
+
|
|
65
|
+
```md
|
|
66
|
+
::widget
|
|
67
|
+
key: value
|
|
68
|
+
nested:
|
|
69
|
+
key: value
|
|
70
|
+
::
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```md
|
|
74
|
+
::widget{title="Short title" layout="4-2"}
|
|
75
|
+
Markdown body here.
|
|
76
|
+
::
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Values are parsed as strings, numbers, or booleans.
|
|
80
|
+
|
|
81
|
+
## Layout
|
|
82
|
+
|
|
83
|
+
Use `::grid` to group widgets. Prefer semantic layouts over CSS strings.
|
|
84
|
+
|
|
85
|
+
```md
|
|
86
|
+
::grid{layout="4-2"}
|
|
87
|
+
::card{title="A"}...::
|
|
88
|
+
::card{title="B"}...::
|
|
89
|
+
::card{title="C"}...::
|
|
90
|
+
::card{title="D"}...::
|
|
91
|
+
::
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Layouts:
|
|
95
|
+
|
|
96
|
+
| Layout | Behavior |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `auto` | Default. Uses child count to choose a reasonable layout. |
|
|
99
|
+
| `1`, `2`, `3`, `4` | Fixed column intent; collapses on narrow containers. |
|
|
100
|
+
| `4-2` | Four columns when wide, two columns when narrow. Good for four cards. |
|
|
101
|
+
| `6-3-2` | Six columns wide, three medium, two narrow. |
|
|
102
|
+
|
|
103
|
+
Grid items stretch to the same row height.
|
|
104
|
+
|
|
105
|
+
A grid cell may itself be a `::grid` (one level of nesting). Use this to put a
|
|
106
|
+
single widget in one column and a vertical stack of widgets in the next:
|
|
107
|
+
|
|
108
|
+
```md
|
|
109
|
+
::grid{layout="2"}
|
|
110
|
+
::chart
|
|
111
|
+
src: left.json
|
|
112
|
+
type: bar-gradient
|
|
113
|
+
::
|
|
114
|
+
::grid{layout="1"}
|
|
115
|
+
::chart
|
|
116
|
+
src: right_a.json
|
|
117
|
+
type: bar-gradient
|
|
118
|
+
::
|
|
119
|
+
::chart
|
|
120
|
+
src: right_b.json
|
|
121
|
+
type: bar-gradient
|
|
122
|
+
::
|
|
123
|
+
::
|
|
124
|
+
::
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Widgets
|
|
128
|
+
|
|
129
|
+
### `card`
|
|
130
|
+
|
|
131
|
+
Generic bordered panel. Body is Markdown, including compact Markdown tables.
|
|
132
|
+
|
|
133
|
+
```md
|
|
134
|
+
::card{title="Model" surface="subtle"}
|
|
135
|
+
**Framework:** DEIM
|
|
136
|
+
|
|
137
|
+
**Checkpoint:** best.pth
|
|
138
|
+
::
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Attrs:
|
|
142
|
+
|
|
143
|
+
| Attr | Values |
|
|
144
|
+
| --- | --- |
|
|
145
|
+
| `title` | Header title. |
|
|
146
|
+
| `subtitle` | Small header subtitle. |
|
|
147
|
+
| `badge` | Pill text on the right. |
|
|
148
|
+
| `badgeValue` | Number used to color the badge. |
|
|
149
|
+
| `surface` | `default`, `subtle`, `dashed`. |
|
|
150
|
+
|
|
151
|
+
### `notice`
|
|
152
|
+
|
|
153
|
+
Lightweight callout or placeholder.
|
|
154
|
+
|
|
155
|
+
```md
|
|
156
|
+
::notice{title="Prediction gallery placeholder" variant="dashed"}
|
|
157
|
+
TODO: add annotated previews.
|
|
158
|
+
::
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Attrs: `title`, `variant` (`default`, `dashed`).
|
|
162
|
+
|
|
163
|
+
### `metricCards`
|
|
164
|
+
|
|
165
|
+
Compact metric tiles from a Markdown table.
|
|
166
|
+
|
|
167
|
+
```md
|
|
168
|
+
::metricCards
|
|
169
|
+
| Label | Value | Description |
|
|
170
|
+
| --- | ---: | --- |
|
|
171
|
+
| mAP | 0.2314 | COCO-style quality. |
|
|
172
|
+
| F1 | 0.3245 | Balance of precision and recall. |
|
|
173
|
+
::
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Columns: `Label`, `Value`, `Description`.
|
|
177
|
+
|
|
178
|
+
### `metricRows`
|
|
179
|
+
|
|
180
|
+
Metric rows with optional help tooltips.
|
|
181
|
+
|
|
182
|
+
```md
|
|
183
|
+
::metricRows{title="Detection Metrics" subtitle="Summary values."}
|
|
184
|
+
| Metric | Value | Help | Description |
|
|
185
|
+
| --- | ---: | --- | --- |
|
|
186
|
+
| Precision | 0.5385 | Correct detections / detections. | Correct predictions. |
|
|
187
|
+
::
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Attrs: `title`, `subtitle`. Columns: `Metric`, `Value`, `Help`,
|
|
191
|
+
`Description`.
|
|
192
|
+
|
|
193
|
+
### `metricCallout`
|
|
194
|
+
|
|
195
|
+
Single highlighted metric plus Markdown body.
|
|
196
|
+
|
|
197
|
+
```md
|
|
198
|
+
::metricCallout{label="Mean IoU" value="0.7782" description="Average overlap quality."}
|
|
199
|
+
Use this together with AP75 to inspect localization.
|
|
200
|
+
::
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Attrs: `label`, `value`, `description`.
|
|
204
|
+
|
|
205
|
+
### `imageGallery`
|
|
206
|
+
|
|
207
|
+
Renders a JSON-backed grid of images. Each tile has a required image layer and
|
|
208
|
+
an optional overlay layer.
|
|
209
|
+
|
|
210
|
+
```md
|
|
211
|
+
::imageGallery
|
|
212
|
+
src: image_gallery.json
|
|
213
|
+
overlayOpacity: 0.7
|
|
214
|
+
::
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"assets": ["image-asset-id", "image-asset-id"],
|
|
220
|
+
"annotations": ["gt-annotation-id", "prediction-annotation-id"]
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
For static report files, use `images` and `overlays` instead:
|
|
225
|
+
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"images": ["gallery/image.jpg"],
|
|
229
|
+
"overlays": ["gallery/overlay.png"]
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Attrs:
|
|
234
|
+
|
|
235
|
+
| Attr | Default |
|
|
236
|
+
| --- | --- |
|
|
237
|
+
| `src` | Required JSON file. |
|
|
238
|
+
| `overlayOpacity` | `0.7` |
|
|
239
|
+
| `fit` | `contain`; use `cover` for cropped tiles. |
|
|
240
|
+
| `aspectRatio` | `4 / 3` |
|
|
241
|
+
| `drawTags` | `true`; set to `false` to render annotation overlays without tag labels (useful for object-detection galleries). |
|
|
242
|
+
|
|
243
|
+
The gallery renders four columns by default and two columns on narrow
|
|
244
|
+
containers. Inline preview shows the first row; when there are more asset-backed
|
|
245
|
+
items, Reportifact emits an `asset-preview` row-click payload for the host to
|
|
246
|
+
open a larger viewer.
|
|
247
|
+
|
|
248
|
+
Static URLs and report-local files work directly. `assets` and `annotations`
|
|
249
|
+
are host-specific refs; pass a `mediaResolver` prop to resolve them. Reportifact
|
|
250
|
+
does not know about OvalBee assets.
|
|
251
|
+
|
|
252
|
+
### `modelComparisonGallery`
|
|
253
|
+
|
|
254
|
+
Renders comparison cards backed by host-resolved image assets. Each card can include
|
|
255
|
+
a ground-truth overlay, a short list of details, and an `asset-preview` payload
|
|
256
|
+
that opens when the card is activated.
|
|
257
|
+
|
|
258
|
+
```md
|
|
259
|
+
::modelComparisonGallery
|
|
260
|
+
src: image_comparison.json
|
|
261
|
+
columns: 3
|
|
262
|
+
previewLimit: 3
|
|
263
|
+
fit: cover
|
|
264
|
+
drawTags: false
|
|
265
|
+
::
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
```json
|
|
269
|
+
{
|
|
270
|
+
"items": [
|
|
271
|
+
{
|
|
272
|
+
"asset": "image-asset-id",
|
|
273
|
+
"annotation": "gt-annotation-id",
|
|
274
|
+
"label": "street_0142.jpg",
|
|
275
|
+
"details": [
|
|
276
|
+
{ "label": "F1 spread", "value": "0.3100" },
|
|
277
|
+
{ "label": "Mean F1", "value": "0.6400" }
|
|
278
|
+
],
|
|
279
|
+
"compare": {
|
|
280
|
+
"action": "asset-preview",
|
|
281
|
+
"layout": "viewer",
|
|
282
|
+
"mode": "side",
|
|
283
|
+
"sync": true,
|
|
284
|
+
"assets": [
|
|
285
|
+
{
|
|
286
|
+
"asset": "image-asset-id",
|
|
287
|
+
"layers": [{ "annotation": "gt-annotation-id", "reference": true }]
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"asset": "image-asset-id",
|
|
291
|
+
"layers": [{ "annotation": "prediction-annotation-id" }]
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Attrs:
|
|
301
|
+
|
|
302
|
+
| Attr | Default |
|
|
303
|
+
| --- | --- |
|
|
304
|
+
| `src` | Required JSON file. |
|
|
305
|
+
| `overlayOpacity` | `0.7` |
|
|
306
|
+
| `fit` | `contain`; use `cover` for cropped cards. |
|
|
307
|
+
| `aspectRatio` | `4 / 3` |
|
|
308
|
+
| `drawTags` | `true` |
|
|
309
|
+
| `columns` | `4`; clamped to `1`–`6`. |
|
|
310
|
+
| `previewLimit` | `4`; clamped to `1`–`24`. |
|
|
311
|
+
|
|
312
|
+
Only visible cards resolve thumbnails. Activating a card emits its `compare`
|
|
313
|
+
payload. **Show all** emits a collection with `click: "comparison"`, preserving
|
|
314
|
+
each item's nested comparison payload. Missing annotations render the base
|
|
315
|
+
image without an overlay.
|
|
316
|
+
|
|
317
|
+
### `chart`
|
|
318
|
+
|
|
319
|
+
Renders a JSON chart file with ECharts.
|
|
320
|
+
|
|
321
|
+
```md
|
|
322
|
+
::chart
|
|
323
|
+
src: key_metrics.json
|
|
324
|
+
type: radar
|
|
325
|
+
title: Key Metrics
|
|
326
|
+
height: 420
|
|
327
|
+
::
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Attrs:
|
|
331
|
+
|
|
332
|
+
| Attr | Values |
|
|
333
|
+
| --- | --- |
|
|
334
|
+
| `src` | JSON file in the report directory. |
|
|
335
|
+
| `type` | `pie`, `line`, `radar`, `bar-normalized`, `bar-gradient`, `bar-grouped`, `bar-stacked`, `heatmap`, `custom-gauge`, `gauge-grade`. |
|
|
336
|
+
| `title` | Chart title override. |
|
|
337
|
+
| `height` | CSS length or number in px. |
|
|
338
|
+
| `radius` | For `pie`; a single value (`"50%"`) renders a solid pie, a comma-separated pair (`"46%,70%"`) renders a donut ring. |
|
|
339
|
+
| `smooth` | For line-like charts; `false` disables smoothing. |
|
|
340
|
+
| `horizontal` | For `bar-gradient`; `true` lays the bars out horizontally (categories on the y-axis). |
|
|
341
|
+
|
|
342
|
+
Charts are full-width by default.
|
|
343
|
+
|
|
344
|
+
Chart data can attach host actions with explicit click IDs. Put `clickId` on
|
|
345
|
+
the rendered data point and map it to an inline payload or report-local JSON
|
|
346
|
+
file in `click.payloads`.
|
|
347
|
+
|
|
348
|
+
```json
|
|
349
|
+
{
|
|
350
|
+
"xAxis": ["horse"],
|
|
351
|
+
"series": [
|
|
352
|
+
{
|
|
353
|
+
"name": "FP",
|
|
354
|
+
"data": [{ "value": 42, "clickId": "outcome.fp.horse" }]
|
|
355
|
+
}
|
|
356
|
+
],
|
|
357
|
+
"click": {
|
|
358
|
+
"version": 2,
|
|
359
|
+
"payloads": {
|
|
360
|
+
"outcome.fp.horse": { "src": "outcome_fp_horse_click.json" }
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Pie charts may use the same click contract by wrapping points in a top-level
|
|
367
|
+
`data` field:
|
|
368
|
+
|
|
369
|
+
```json
|
|
370
|
+
{
|
|
371
|
+
"data": [{ "name": "Annotated", "value": 12, "clickId": "coverage.annotated" }],
|
|
372
|
+
"click": {
|
|
373
|
+
"version": 2,
|
|
374
|
+
"payloads": {
|
|
375
|
+
"coverage.annotated": { "payload": { "action": "asset-preview", "layout": "collection", "assets": [] } }
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
For external payloads, Reportifact emits `{ "action": "load-report-payload", "src": "..." }`.
|
|
382
|
+
The host resolves that report-local JSON file, then handles the loaded payload
|
|
383
|
+
the same way it handles table row-click payloads.
|
|
384
|
+
|
|
385
|
+
### `modelClassComparison`
|
|
386
|
+
|
|
387
|
+
Compares per-class metrics across two or more ML models on a shared horizontal
|
|
388
|
+
scale. The first metric is active initially, and a compact switcher selects
|
|
389
|
+
another metric.
|
|
390
|
+
|
|
391
|
+
```md
|
|
392
|
+
::modelClassComparison
|
|
393
|
+
src: per_class_matrix.json
|
|
394
|
+
::
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
```json
|
|
398
|
+
{
|
|
399
|
+
"min": 0,
|
|
400
|
+
"max": 1,
|
|
401
|
+
"models": [
|
|
402
|
+
{ "id": "report-a", "name": "Detector A", "color": "#0072B2" },
|
|
403
|
+
{ "id": "report-b", "name": "Detector B", "color": "#E69F00" }
|
|
404
|
+
],
|
|
405
|
+
"metrics": [
|
|
406
|
+
{ "key": "ap", "label": "AP" },
|
|
407
|
+
{ "key": "f1", "label": "F1" }
|
|
408
|
+
],
|
|
409
|
+
"classes": [
|
|
410
|
+
{
|
|
411
|
+
"name": "car",
|
|
412
|
+
"values": {
|
|
413
|
+
"report-a": { "ap": 0.82, "f1": 0.78 },
|
|
414
|
+
"report-b": { "ap": 0.76, "f1": 0.74 }
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
]
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Model IDs and metric keys must be unique. Colors must be six-digit hex values.
|
|
422
|
+
Models use circular markers distinguished by color. Overlapping markers collapse
|
|
423
|
+
into an accessible cluster that exposes each exact value on hover, focus, or tap.
|
|
424
|
+
With up to eight classes the full matrix is shown. Larger matrices add search
|
|
425
|
+
and ten-row pagination. Rows are sorted alphabetically, and malformed rows
|
|
426
|
+
without a comparable metric are ignored.
|
|
427
|
+
|
|
428
|
+
### `table`
|
|
429
|
+
|
|
430
|
+
Server-backed Parquet table with filtering, sorting, pagination, and optional
|
|
431
|
+
row clicks.
|
|
432
|
+
|
|
433
|
+
```md
|
|
434
|
+
::table
|
|
435
|
+
src: per_image_metrics.parquet
|
|
436
|
+
pageSize: 20
|
|
437
|
+
fixFirstColumn: true
|
|
438
|
+
::
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Attrs:
|
|
442
|
+
|
|
443
|
+
| Attr | Default |
|
|
444
|
+
| --- | --- |
|
|
445
|
+
| `src` | Required Parquet file. |
|
|
446
|
+
| `pageSize` | `10` |
|
|
447
|
+
| `fixFirstColumn` | `true` |
|
|
448
|
+
| `hideHeader` | `false` |
|
|
449
|
+
| `columnTooltips` | Optional map of column name to tooltip text. |
|
|
450
|
+
|
|
451
|
+
Row clicks: include a `$row_click` column in the Parquet row. The value should
|
|
452
|
+
be a JSON string or object. Reportifact emits it through `row-click`. Columns
|
|
453
|
+
whose names start with `$` are treated as system columns and are hidden from the
|
|
454
|
+
rendered table.
|
|
455
|
+
|
|
456
|
+
Common OvalBee payload — the single host contract is `action: "asset-preview"`
|
|
457
|
+
(see `.agents/spec/asset-preview.md` for the full contract). It expresses a
|
|
458
|
+
direct single-asset preview, a synchronized side-by-side comparison, and a
|
|
459
|
+
browsable collection grid in one shape via `assets[]` and per-asset `layers[]`:
|
|
460
|
+
|
|
461
|
+
```json
|
|
462
|
+
{
|
|
463
|
+
"action": "asset-preview",
|
|
464
|
+
"layout": "viewer",
|
|
465
|
+
"mode": "side",
|
|
466
|
+
"sync": true,
|
|
467
|
+
"assets": [
|
|
468
|
+
{ "asset": "asset-id-1", "label": "Ground Truth", "layers": [{ "annotation": "gt-ann-id", "reference": true }] },
|
|
469
|
+
{ "asset": "asset-id-2", "label": "Prediction", "layers": [{ "mask": "mask-id-1" }] }
|
|
470
|
+
]
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
A layer's overlay comes from `annotation` (server-rendered from an OvalBee
|
|
475
|
+
annotation asset), `mask` (a ready overlay asset), or `maskUrl` (a ready overlay
|
|
476
|
+
url); a layer may be missing entirely for a pane with no overlay (e.g. a
|
|
477
|
+
negative image with no ground-truth annotation).
|
|
478
|
+
|
|
479
|
+
`imageGallery` emits an `asset-preview` collection payload when the user clicks
|
|
480
|
+
its show-all button:
|
|
481
|
+
|
|
482
|
+
```json
|
|
483
|
+
{
|
|
484
|
+
"action": "asset-preview",
|
|
485
|
+
"layout": "collection",
|
|
486
|
+
"title": "Images",
|
|
487
|
+
"click": "preview",
|
|
488
|
+
"grid": 4,
|
|
489
|
+
"assets": [
|
|
490
|
+
{ "asset": "asset-id-1", "layers": [{ "annotation": "annotation-id-1" }] },
|
|
491
|
+
{ "asset": "asset-id-2", "layers": [{ "annotation": "annotation-id-2" }] }
|
|
492
|
+
]
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
For large media grids or chart clicks, each tile can carry its own crop and a
|
|
497
|
+
nested `compare` drill-down (a full `asset-preview` payload opened when that
|
|
498
|
+
tile is clicked and `click: "comparison"`):
|
|
499
|
+
|
|
500
|
+
```json
|
|
501
|
+
{
|
|
502
|
+
"action": "asset-preview",
|
|
503
|
+
"layout": "collection",
|
|
504
|
+
"title": "False positives: horse",
|
|
505
|
+
"click": "comparison",
|
|
506
|
+
"assets": [
|
|
507
|
+
{
|
|
508
|
+
"asset": "image-asset-id",
|
|
509
|
+
"crop": [10, 20, 120, 80],
|
|
510
|
+
"layers": [{ "annotation": "prediction-annotation-id" }],
|
|
511
|
+
"compare": {
|
|
512
|
+
"action": "asset-preview",
|
|
513
|
+
"layout": "viewer",
|
|
514
|
+
"mode": "side",
|
|
515
|
+
"sync": true,
|
|
516
|
+
"grid": 2,
|
|
517
|
+
"assets": [
|
|
518
|
+
{ "asset": "image-asset-id", "crop": [10, 20, 120, 80], "layers": [{ "annotation": "gt-annotation-id", "reference": true }] },
|
|
519
|
+
{ "asset": "image-asset-id", "crop": [10, 20, 120, 80], "layers": [{ "annotation": "prediction-annotation-id" }] }
|
|
520
|
+
]
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
]
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
`layout: "collection"` opens a browsable thumbnail grid first; `layout: "viewer"`
|
|
528
|
+
opens the viewer directly. For a table row that represents one image, use a
|
|
529
|
+
direct single-asset viewer payload:
|
|
530
|
+
|
|
531
|
+
```json
|
|
532
|
+
{
|
|
533
|
+
"action": "asset-preview",
|
|
534
|
+
"layout": "viewer",
|
|
535
|
+
"assets": [
|
|
536
|
+
{ "asset": "image-asset-id", "layers": [{ "annotation": "annotation-id", "label": "main / supervisely" }] }
|
|
537
|
+
]
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
Python report nodes build these payloads via the shared builder in the OvalBee
|
|
542
|
+
SDK (`ovalbee.report.asset_preview`: `single_asset_preview`, `compare_payload`,
|
|
543
|
+
`media_grid_payload`, `asset_spec`, `layer`) rather than hand-writing dicts.
|
|
544
|
+
|
|
545
|
+
### `timeline`
|
|
546
|
+
|
|
547
|
+
Per-row horizontal time tracks with point markers, rendered with ECharts custom
|
|
548
|
+
series. Built for "one bar per video with cut points" visuals, but any
|
|
549
|
+
row/track/marker data fits.
|
|
550
|
+
|
|
551
|
+
```md
|
|
552
|
+
::timeline
|
|
553
|
+
src: all_videos_timeline.json
|
|
554
|
+
title: All-videos timeline
|
|
555
|
+
timeFormat: clock
|
|
556
|
+
::
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
```json
|
|
560
|
+
{
|
|
561
|
+
"rows": [
|
|
562
|
+
{
|
|
563
|
+
"id": "asset-id",
|
|
564
|
+
"name": "2026_01_15_Umaker_L9",
|
|
565
|
+
"group": "good",
|
|
566
|
+
"start": 0,
|
|
567
|
+
"end": 248,
|
|
568
|
+
"cuts": [62, 131, 198]
|
|
569
|
+
}
|
|
570
|
+
]
|
|
571
|
+
}
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
Row fields: `name` and `end` are required; `start` defaults to `0`; `cuts` are
|
|
575
|
+
marker positions in seconds; `group` colors the track from a built-in palette
|
|
576
|
+
(one legend entry per group, rows without a group share one series); `color`
|
|
577
|
+
overrides the track color for that row; `click` is an optional payload object
|
|
578
|
+
emitted through `row-click` when the row's track or a marker is clicked (same
|
|
579
|
+
host contract as table `$row_click`, e.g. an `asset-preview` payload).
|
|
580
|
+
|
|
581
|
+
Attrs:
|
|
582
|
+
|
|
583
|
+
| Attr | Values |
|
|
584
|
+
| --- | --- |
|
|
585
|
+
| `src` | JSON file in the report directory. |
|
|
586
|
+
| `title` | Chart title. |
|
|
587
|
+
| `timeFormat` | Initial time-axis mode: `seconds` (default) or `clock` (`mm:ss`, switching to `hh:mm:ss` past one hour). A toolbar toggle lets the viewer change it. |
|
|
588
|
+
| `zoom` | `true` enables x-axis zooming (`inside` + slider). Off by default. |
|
|
589
|
+
|
|
590
|
+
Hovering a track shows the row name, group, duration, and its cut list;
|
|
591
|
+
hovering a marker shows that specific cut time. Widget height grows with the
|
|
592
|
+
number of rows.
|
|
593
|
+
|
|
594
|
+
### `markdown`
|
|
595
|
+
|
|
596
|
+
Loads and renders another Markdown file from the report directory.
|
|
597
|
+
|
|
598
|
+
```md
|
|
599
|
+
::markdown
|
|
600
|
+
src: details.md
|
|
601
|
+
live: true
|
|
602
|
+
::
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
Attrs: `src`, `live` (`true` enables refresh).
|
|
606
|
+
|
|
607
|
+
### `tensorboard`
|
|
608
|
+
|
|
609
|
+
Renders TensorBoard scalar event data.
|
|
610
|
+
|
|
611
|
+
```md
|
|
612
|
+
::tensorboard
|
|
613
|
+
src: events.out.tfevents
|
|
614
|
+
title: Training
|
|
615
|
+
height: 420
|
|
616
|
+
smooth: true
|
|
617
|
+
maxPoints: 1000
|
|
618
|
+
::
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
Attrs: `src`, `title`, `height`, `smooth`, `maxPoints`.
|
|
622
|
+
|
|
623
|
+
### `metricsTable`
|
|
624
|
+
|
|
625
|
+
JSON-backed metrics/table visualization. Use this when the report already emits
|
|
626
|
+
the metrics JSON shape expected by the widget.
|
|
627
|
+
|
|
628
|
+
```md
|
|
629
|
+
::metricsTable
|
|
630
|
+
src: metrics.json
|
|
631
|
+
title: Metrics
|
|
632
|
+
show_pct: true
|
|
633
|
+
::
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
### `scope`
|
|
637
|
+
|
|
638
|
+
Renders a sticky "Report scoped to" breadcrumb control above the section
|
|
639
|
+
navigation and re-renders the **whole** report for the selected data scope. A
|
|
640
|
+
scope is a subset of the evaluated data identified by an asset-tag value (e.g.
|
|
641
|
+
`split.bad_weather`); the base scope is `all` (the whole dataset).
|
|
642
|
+
|
|
643
|
+
Place the directive in the **preamble** (before the first `# H1`). It is hoisted
|
|
644
|
+
above the table of contents and is not rendered as a section.
|
|
645
|
+
|
|
646
|
+
```md
|
|
647
|
+
::scope
|
|
648
|
+
src: scopes.json
|
|
649
|
+
::
|
|
650
|
+
|
|
651
|
+
# Overview
|
|
652
|
+
...
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
`src` points to a manifest describing the tags, their values, and per-scope asset
|
|
656
|
+
counts:
|
|
657
|
+
|
|
658
|
+
```json
|
|
659
|
+
{
|
|
660
|
+
"all": { "n": 50 },
|
|
661
|
+
"tags": [
|
|
662
|
+
{
|
|
663
|
+
"key": "split",
|
|
664
|
+
"label": "split",
|
|
665
|
+
"values": [
|
|
666
|
+
{ "value": "bad_weather", "scopeId": "split.bad_weather", "n": 24 },
|
|
667
|
+
{ "value": "good", "scopeId": "split.good", "n": 26 }
|
|
668
|
+
]
|
|
669
|
+
}
|
|
670
|
+
]
|
|
671
|
+
}
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
When a scope is active, the report updates in place (no remount):
|
|
675
|
+
|
|
676
|
+
- **File-backed widgets** (`chart`, `table`, `imageGallery`, `modelComparisonGallery`,
|
|
677
|
+
`tensorboard`, `metricsTable`, `modelClassComparison`) load a per-scope source:
|
|
678
|
+
the suffix `.<scopeId>` is inserted
|
|
679
|
+
before the extension, so `outcome_counts.json` becomes
|
|
680
|
+
`outcome_counts.split.bad_weather.json`. The base scope uses the unsuffixed
|
|
681
|
+
file. If a per-scope file is absent from the report, the base file is used.
|
|
682
|
+
- **Text** (Markdown prose, `card` bodies, `metricCards` / `metricRows` tables,
|
|
683
|
+
badges, titles) resolves `$name` / `${name}` placeholders from
|
|
684
|
+
`vars.<scopeId>.json` merged over the base `vars.json`. Authors keep
|
|
685
|
+
placeholders in `index.md` (instead of substituting at build time) for any
|
|
686
|
+
value that varies per scope; literal text stays static. Tokens with no matching
|
|
687
|
+
key in the vars dictionary are left untouched, so non-scoped reports
|
|
688
|
+
(already substituted, no `vars.json`) render unchanged.
|
|
689
|
+
|
|
690
|
+
`vars.json` is a flat dictionary, e.g.:
|
|
691
|
+
|
|
692
|
+
```json
|
|
693
|
+
{ "map": 55.1, "ap50": 95.9, "f1": 92.4, "n_images": 50, "n_gt": 1781 }
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
A report without a `::scope` directive (and without `vars.json`) behaves
|
|
697
|
+
exactly as before.
|
|
698
|
+
|
|
699
|
+
## Embedding
|
|
700
|
+
|
|
701
|
+
```vue
|
|
702
|
+
<script setup lang="ts">
|
|
703
|
+
import { Reportifact } from '@ovalbee/reportifact';
|
|
704
|
+
</script>
|
|
705
|
+
|
|
706
|
+
<template>
|
|
707
|
+
<Reportifact
|
|
708
|
+
:url-map="urlMap"
|
|
709
|
+
base-path="/api/reports"
|
|
710
|
+
:media-resolver="resolveMedia"
|
|
711
|
+
:custom-widgets="customWidgets"
|
|
712
|
+
@row-click="onRowClick"
|
|
713
|
+
/>
|
|
714
|
+
</template>
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
Important props:
|
|
718
|
+
|
|
719
|
+
| Prop | Purpose |
|
|
720
|
+
| --- | --- |
|
|
721
|
+
| `url` | Direct URL to `index.md`. |
|
|
722
|
+
| `urlMap` | Map file names to signed/public URLs. Uses `index.md` for the schema. |
|
|
723
|
+
| `linkUrlMap` | Optional map for Markdown links. |
|
|
724
|
+
| `basePath` | API route base for Parquet and TensorBoard proxy routes. |
|
|
725
|
+
| `scrollContainer` | Scroll container used for active section tracking. |
|
|
726
|
+
| `refreshInterval` | Refresh interval for live report widgets. |
|
|
727
|
+
| `mediaResolver` | Optional resolver for gallery `assets` and `annotations`. |
|
|
728
|
+
| `customWidgets` | Optional Vue 3 components keyed by directive name for host-owned widgets. |
|
|
729
|
+
|
|
730
|
+
## Portable Widgets
|
|
731
|
+
|
|
732
|
+
Sites and other Vue hosts can render selected widgets without loading the full
|
|
733
|
+
Markdown report runtime. Import every widget from its direct subpath; there is
|
|
734
|
+
intentionally no runtime `widgets` barrel because it would pull ECharts into
|
|
735
|
+
pages that only use metric components.
|
|
736
|
+
|
|
737
|
+
```vue
|
|
738
|
+
<script setup lang="ts">
|
|
739
|
+
import ReportifactFigure from '@ovalbee/reportifact/widgets/figure';
|
|
740
|
+
import ReportifactChart from '@ovalbee/reportifact/widgets/chart';
|
|
741
|
+
import '@ovalbee/reportifact/style.css';
|
|
742
|
+
|
|
743
|
+
const chart = {
|
|
744
|
+
xAxis: ['YOLO', 'DEIM'],
|
|
745
|
+
data: [0.71, 0.76],
|
|
746
|
+
};
|
|
747
|
+
</script>
|
|
748
|
+
|
|
749
|
+
<template>
|
|
750
|
+
<ReportifactFigure
|
|
751
|
+
title="Validation mAP"
|
|
752
|
+
caption="DEIM leads on the held-out validation set."
|
|
753
|
+
fallback-src="/case-studies/yolo-vs-deim/map.webp"
|
|
754
|
+
>
|
|
755
|
+
<ReportifactChart type="bar-gradient" :data="chart" />
|
|
756
|
+
</ReportifactFigure>
|
|
757
|
+
</template>
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
Public subpaths:
|
|
761
|
+
|
|
762
|
+
| Import | Component | Portable input |
|
|
763
|
+
| --- | --- | --- |
|
|
764
|
+
| `@ovalbee/reportifact/widgets/host` | `ReportifactWidgetHost` | Theme and loading-style boundary. |
|
|
765
|
+
| `@ovalbee/reportifact/widgets/figure` | `ReportifactFigure` | Semantic title/caption, loading, error fallback, source link. |
|
|
766
|
+
| `@ovalbee/reportifact/widgets/chart` | `ReportifactChart` | `src` JSON URL or inline `data`. |
|
|
767
|
+
| `@ovalbee/reportifact/widgets/metric-cards` | `ReportifactMetricCards` | Inline `data` or Markdown-table `content`. |
|
|
768
|
+
| `@ovalbee/reportifact/widgets/metric-callout` | `ReportifactMetricCallout` | Inline `data` or direct props. |
|
|
769
|
+
| `@ovalbee/reportifact/widgets/metrics-table` | `ReportifactMetricsTable` | `src` JSON URL or inline `data`. |
|
|
770
|
+
| `@ovalbee/reportifact/widgets/timeline` | `ReportifactTimeline` | `src` JSON URL or inline `data`. |
|
|
771
|
+
| `@ovalbee/reportifact/widgets/image-gallery` | `ReportifactImageGallery` | `src` JSON URL or inline `data`; optional media resolver. |
|
|
772
|
+
| `@ovalbee/reportifact/widgets/contracts` | Type-only contracts | Widget data and resolver types. |
|
|
773
|
+
|
|
774
|
+
For URL-backed widgets, `src` is the portable spelling and legacy `url` remains
|
|
775
|
+
supported for the full report renderer. Inline `data` takes precedence when
|
|
776
|
+
both are present. `ReportifactFigure` owns the shared loading and fallback
|
|
777
|
+
presentation; its `fallbackSrc` is also emitted as a no-JavaScript fallback.
|
|
778
|
+
Wrap lower-level widgets in `ReportifactWidgetHost` when they are not already
|
|
779
|
+
inside a `ReportifactFigure` or full `Reportifact` renderer.
|
|
780
|
+
|
|
781
|
+
`MetricCallout`'s `html` field is trusted pre-rendered HTML. Do not pass
|
|
782
|
+
untrusted user input without sanitizing it first.
|
|
783
|
+
|
|
784
|
+
Parquet tables, TensorBoard, scopes, and the full widget dispatcher remain
|
|
785
|
+
full-report features. Embed a public full report for those experiences instead
|
|
786
|
+
of importing their internals into a content page.
|
|
787
|
+
|
|
788
|
+
### Custom Widgets
|
|
789
|
+
|
|
790
|
+
Hosts can register Vue 3 components for directives that Reportifact does not
|
|
791
|
+
own. The component is compiled into the host app bundle; the Markdown only names
|
|
792
|
+
the widget and provides parsed options.
|
|
793
|
+
|
|
794
|
+
```vue
|
|
795
|
+
<script setup lang="ts">
|
|
796
|
+
import { Reportifact, type ReportifactCustomWidgets } from '@ovalbee/reportifact';
|
|
797
|
+
import ModelCheckpointsWidget from './custom-widgets/ModelCheckpointsWidget.vue';
|
|
798
|
+
|
|
799
|
+
const customWidgets: ReportifactCustomWidgets = {
|
|
800
|
+
modelCheckpoints: ModelCheckpointsWidget,
|
|
801
|
+
};
|
|
802
|
+
</script>
|
|
803
|
+
|
|
804
|
+
<template>
|
|
805
|
+
<Reportifact :url-map="urlMap" :custom-widgets="customWidgets" />
|
|
806
|
+
</template>
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
The registered component receives:
|
|
810
|
+
|
|
811
|
+
| Prop | Purpose |
|
|
812
|
+
| --- | --- |
|
|
813
|
+
| `widget` | Parsed widget item, including `widgetType`, `content`, and `options`. |
|
|
814
|
+
| `url` | Resolved `src` URL, when the directive has one. |
|
|
815
|
+
| `options` | Parsed directive options. |
|
|
816
|
+
| `html` | Markdown-rendered widget body. |
|
|
817
|
+
| `context` | Helpers: `resolveUrl`, `resolveLinkUrl`, `mediaResolver`, `emitRowClick`. |
|
|
818
|
+
|
|
819
|
+
Custom widgets may expose `refresh()`; Reportifact calls it during manual or
|
|
820
|
+
interval refresh. Unknown unregistered directives render an unsupported-widget
|
|
821
|
+
placeholder instead of failing the report.
|
|
822
|
+
|
|
823
|
+
Example host-owned directive:
|
|
824
|
+
|
|
825
|
+
```md
|
|
826
|
+
::modelCheckpoints
|
|
827
|
+
assetId: 3f2504e0-4f89-11d3-9a0c-0305e82c3301
|
|
828
|
+
::
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
## Development
|
|
832
|
+
|
|
833
|
+
```sh
|
|
834
|
+
npm run build
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
From this repo, prefer the containerized check:
|
|
838
|
+
|
|
839
|
+
```sh
|
|
840
|
+
docker compose exec web sh -lc 'cd /workspace/packages/reportifact && npm run build'
|
|
841
|
+
```
|