plotleaf 0.2.0 → 0.3.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 +53 -7
- package/dist/assets/base.js +1 -1
- package/dist/assets/charts.js +1 -1
- package/dist/assets/styles.css +71 -0
- package/dist/cli.js +493 -121
- package/examples/catalog/assets/desk-lamp.jpg +0 -0
- package/examples/catalog/assets/field-notebook.png +0 -0
- package/examples/catalog/assets/linen-tote.png +0 -0
- package/examples/catalog/products.json +78 -0
- package/examples/catalog/report.md +39 -0
- package/examples/catalog/showcase-products.json +78 -0
- package/examples/report.md +96 -0
- package/package.json +9 -2
- package/schema/components.json +34 -5
- package/skills/plotleaf/SKILL.md +22 -5
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"product": "Linen tote",
|
|
4
|
+
"image": "./assets/linen-tote.png",
|
|
5
|
+
"image_alt": "Illustration of a sand-coloured linen tote",
|
|
6
|
+
"weekly_orders": [
|
|
7
|
+
42,
|
|
8
|
+
47,
|
|
9
|
+
44,
|
|
10
|
+
56,
|
|
11
|
+
59,
|
|
12
|
+
63,
|
|
13
|
+
68,
|
|
14
|
+
74
|
|
15
|
+
],
|
|
16
|
+
"orders": 453,
|
|
17
|
+
"revenue": 18120,
|
|
18
|
+
"change": 0.182,
|
|
19
|
+
"last_week": 74
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"product": "Desk lamp",
|
|
23
|
+
"image": "./assets/desk-lamp.jpg",
|
|
24
|
+
"image_alt": "Illustration of a blue desk lamp",
|
|
25
|
+
"weekly_orders": [
|
|
26
|
+
38,
|
|
27
|
+
35,
|
|
28
|
+
39,
|
|
29
|
+
null,
|
|
30
|
+
46,
|
|
31
|
+
44,
|
|
32
|
+
49,
|
|
33
|
+
53
|
|
34
|
+
],
|
|
35
|
+
"orders": 304,
|
|
36
|
+
"revenue": 27360,
|
|
37
|
+
"change": 0.124,
|
|
38
|
+
"last_week": 53
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"product": "Field notebook",
|
|
42
|
+
"image": "./assets/field-notebook.png",
|
|
43
|
+
"image_alt": "Illustration of a green field notebook",
|
|
44
|
+
"weekly_orders": [
|
|
45
|
+
60,
|
|
46
|
+
63,
|
|
47
|
+
59,
|
|
48
|
+
64,
|
|
49
|
+
62,
|
|
50
|
+
69,
|
|
51
|
+
72,
|
|
52
|
+
78
|
|
53
|
+
],
|
|
54
|
+
"orders": 527,
|
|
55
|
+
"revenue": 10540,
|
|
56
|
+
"change": 0.068,
|
|
57
|
+
"last_week": 78
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"product": "Linen tote \u2014 natural",
|
|
61
|
+
"image": "./assets/linen-tote.png",
|
|
62
|
+
"image_alt": "Illustration of the same sand-coloured linen tote in a second product row",
|
|
63
|
+
"weekly_orders": [
|
|
64
|
+
18,
|
|
65
|
+
21,
|
|
66
|
+
20,
|
|
67
|
+
24,
|
|
68
|
+
23,
|
|
69
|
+
27,
|
|
70
|
+
29,
|
|
71
|
+
31
|
|
72
|
+
],
|
|
73
|
+
"orders": 193,
|
|
74
|
+
"revenue": 7720,
|
|
75
|
+
"change": 0.094,
|
|
76
|
+
"last_week": 31
|
|
77
|
+
}
|
|
78
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Product Performance
|
|
3
|
+
description: A fictional catalogue with embedded product images and weekly order sparklines.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Product Performance
|
|
7
|
+
|
|
8
|
+
A fictional store's product performance over **eight weeks ending 1 November 2026**. All data, comparisons and product illustrations are synthetic and prepared for this example.
|
|
9
|
+
|
|
10
|
+
:::grid{columns="3"}
|
|
11
|
+
::metric{label="Orders" value="1477" comparison="18.2% vs previous eight weeks" direction="up" sentiment="positive"}
|
|
12
|
+
::metric{label="Revenue" value="63740" format="currency:USD" comparison="20% vs previous eight weeks" direction="up" sentiment="positive"}
|
|
13
|
+
::metric{label="Products" value="4" comparison="1 more than previous period" direction="up" sentiment="positive"}
|
|
14
|
+
:::
|
|
15
|
+
|
|
16
|
+
The fictional comparison period contains **1,250 orders**, **US$53,120 revenue** and **three products**. Changes above are prepared and rounded by the example author.
|
|
17
|
+
|
|
18
|
+
## Product detail
|
|
19
|
+
|
|
20
|
+
Weekly order lines share the same vertical scale, so their heights are comparable. The lamp has one missing week, shown as a gap; its prepared total covers recorded weeks only. Expand **View values** to inspect the original samples.
|
|
21
|
+
|
|
22
|
+
:::table{data="./products.json" title="Performance by product"}
|
|
23
|
+
::column{field="image" label="Preview" kind="image" alt="image_alt"}
|
|
24
|
+
::column{field="product" label="Product"}
|
|
25
|
+
::column{field="weekly_orders" label="Weekly orders" kind="sparkline" sortBy="last_week"}
|
|
26
|
+
::column{field="orders" label="Orders" format="number"}
|
|
27
|
+
::column{field="revenue" label="Revenue" format="currency:USD"}
|
|
28
|
+
::column{field="change" label="Δ period" format="percent" sign="always" semantics="higher-is-better"}
|
|
29
|
+
:::
|
|
30
|
+
|
|
31
|
+
### Reading this table
|
|
32
|
+
|
|
33
|
+
- **Linen tote leads relative growth.** Comparisons are prepared by the author, not calculated from the sparkline.
|
|
34
|
+
- **Notebooks lead order volume.** They also have the highest final-week value in this fictional set.
|
|
35
|
+
- **The natural tote reuses its image.** The portable report embeds each unique illustration once.
|
|
36
|
+
|
|
37
|
+
:::callout{title="Coverage"}
|
|
38
|
+
Every value and illustration is fictional. Sorting Weekly orders uses the declared final-week key; it does not compare arrays or recompute totals. No live filters or subtotal calculations are implied.
|
|
39
|
+
:::
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"product": "Linen tote",
|
|
4
|
+
"image": "./catalog/assets/linen-tote.png",
|
|
5
|
+
"image_alt": "Illustration of a sand-coloured linen tote",
|
|
6
|
+
"weekly_orders": [
|
|
7
|
+
42,
|
|
8
|
+
47,
|
|
9
|
+
44,
|
|
10
|
+
56,
|
|
11
|
+
59,
|
|
12
|
+
63,
|
|
13
|
+
68,
|
|
14
|
+
74
|
|
15
|
+
],
|
|
16
|
+
"orders": 453,
|
|
17
|
+
"revenue": 18120,
|
|
18
|
+
"change": 0.182,
|
|
19
|
+
"last_week": 74
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"product": "Desk lamp",
|
|
23
|
+
"image": "./catalog/assets/desk-lamp.jpg",
|
|
24
|
+
"image_alt": "Illustration of a blue desk lamp",
|
|
25
|
+
"weekly_orders": [
|
|
26
|
+
38,
|
|
27
|
+
35,
|
|
28
|
+
39,
|
|
29
|
+
null,
|
|
30
|
+
46,
|
|
31
|
+
44,
|
|
32
|
+
49,
|
|
33
|
+
53
|
|
34
|
+
],
|
|
35
|
+
"orders": 304,
|
|
36
|
+
"revenue": 27360,
|
|
37
|
+
"change": 0.124,
|
|
38
|
+
"last_week": 53
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"product": "Field notebook",
|
|
42
|
+
"image": "./catalog/assets/field-notebook.png",
|
|
43
|
+
"image_alt": "Illustration of a green field notebook",
|
|
44
|
+
"weekly_orders": [
|
|
45
|
+
60,
|
|
46
|
+
63,
|
|
47
|
+
59,
|
|
48
|
+
64,
|
|
49
|
+
62,
|
|
50
|
+
69,
|
|
51
|
+
72,
|
|
52
|
+
78
|
|
53
|
+
],
|
|
54
|
+
"orders": 527,
|
|
55
|
+
"revenue": 10540,
|
|
56
|
+
"change": 0.068,
|
|
57
|
+
"last_week": 78
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"product": "Linen tote — natural",
|
|
61
|
+
"image": "./catalog/assets/linen-tote.png",
|
|
62
|
+
"image_alt": "Illustration of the same sand-coloured linen tote in a second product row",
|
|
63
|
+
"weekly_orders": [
|
|
64
|
+
18,
|
|
65
|
+
21,
|
|
66
|
+
20,
|
|
67
|
+
24,
|
|
68
|
+
23,
|
|
69
|
+
27,
|
|
70
|
+
29,
|
|
71
|
+
31
|
|
72
|
+
],
|
|
73
|
+
"orders": 193,
|
|
74
|
+
"revenue": 7720,
|
|
75
|
+
"change": 0.094,
|
|
76
|
+
"last_week": 31
|
|
77
|
+
}
|
|
78
|
+
]
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Storefront Overview
|
|
3
|
+
description: A cumulative plotleaf showcase with fictional metrics, charts, tables, images and sparklines.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Storefront Overview
|
|
7
|
+
|
|
8
|
+
A fictional store's weekly performance, using synthetic data for demonstration. Orders grew **18.2%**, while returns fell **12%** from the previous period.
|
|
9
|
+
|
|
10
|
+
:::grid{columns="4"}
|
|
11
|
+
::metric{label="Orders" value="12480" comparison="18.2% vs previous week" direction="up" sentiment="positive"}
|
|
12
|
+
::metric{label="Revenue" value="824160" format="currency:USD" comparison="24.1% vs previous week" direction="up" sentiment="positive"}
|
|
13
|
+
::metric{label="Returns" value="384" comparison="12% vs previous week" direction="down" sentiment="positive"}
|
|
14
|
+
::metric{label="Return rate" value="0.03076923076923077" format="percent" comparison="0.4 pt vs previous week" direction="down" sentiment="positive"}
|
|
15
|
+
:::
|
|
16
|
+
|
|
17
|
+
## Sales and returns
|
|
18
|
+
|
|
19
|
+
The store received **12,480 orders** over seven days. Size-related returns made up most of the **384 returns**, suggesting a useful area for clearer product guidance.
|
|
20
|
+
|
|
21
|
+
:::grid{columns="2"}
|
|
22
|
+
::area{data="./storefront/orders.json" x="date" y="orders" xType="date" title="Orders by day" target="2000" targetLabel="Daily target"}
|
|
23
|
+
::bar{data="./storefront/returns.json" x="reason" y="count" title="Returns by reason"}
|
|
24
|
+
:::
|
|
25
|
+
|
|
26
|
+
## Sales by category
|
|
27
|
+
|
|
28
|
+
Apparel accounts for the largest share of this fictional week's **12,480 orders**. Each order belongs to one category.
|
|
29
|
+
|
|
30
|
+
::::grid{columns="2"}
|
|
31
|
+
::donut{data="./storefront/categories.json" category="category" value="orders" title="Order mix"}
|
|
32
|
+
:::table{data="./storefront/categories.json" title="Category performance"}
|
|
33
|
+
::column{field="category" label="Category"}
|
|
34
|
+
::column{field="orders" label="Orders" format="number"}
|
|
35
|
+
::column{field="revenue" label="Revenue" format="currency:USD"}
|
|
36
|
+
::column{field="change" label="Δ week" format="percent" sign="always" semantics="higher-is-better"}
|
|
37
|
+
:::
|
|
38
|
+
::::
|
|
39
|
+
|
|
40
|
+
### Key findings
|
|
41
|
+
|
|
42
|
+
- **Growth spans every category.** All four categories increased orders over the previous week.
|
|
43
|
+
- **Returns declined.** The return count and rate both improved.
|
|
44
|
+
- **Size guidance is the next opportunity.** Review fit descriptions before changing the return policy.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Product onboarding
|
|
49
|
+
|
|
50
|
+
A separate fictional product example covers **6 April–28 June 2026**. Its prepared funnel uses **41,200 visitors** as the denominator for every stage; its statuses reflect the example author's operating targets. These team counts are independent of the store's orders above.
|
|
51
|
+
|
|
52
|
+
:::grid{columns="2"}
|
|
53
|
+
::line{data="./onboarding/signups.json" x="week" y="signups" xType="date" title="Signups by week" target="1600" targetLabel="Weekly target"}
|
|
54
|
+
::funnel{data="./onboarding/funnel.json" stage="stage" value="teams" total="baseline" share="share" status="status" title="Funnel by stage"}
|
|
55
|
+
:::
|
|
56
|
+
|
|
57
|
+
### Onboarding findings
|
|
58
|
+
|
|
59
|
+
Activation needs attention against the prepared target, and conversion is marked at risk. Expand the chart and funnel data disclosures to inspect the original values and denominators.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Product catalogue
|
|
64
|
+
|
|
65
|
+
A fictional store's product performance over **eight weeks ending 1 November 2026**. All data, comparisons and product illustrations are synthetic and prepared for this example.
|
|
66
|
+
|
|
67
|
+
:::grid{columns="3"}
|
|
68
|
+
::metric{label="Orders" value="1477" comparison="18.2% vs previous eight weeks" direction="up" sentiment="positive"}
|
|
69
|
+
::metric{label="Revenue" value="63740" format="currency:USD" comparison="20% vs previous eight weeks" direction="up" sentiment="positive"}
|
|
70
|
+
::metric{label="Products" value="4" comparison="1 more than previous period" direction="up" sentiment="positive"}
|
|
71
|
+
:::
|
|
72
|
+
|
|
73
|
+
The fictional comparison period contains **1,250 orders**, **US$53,120 revenue** and **three products**. Changes above are prepared and rounded by the example author.
|
|
74
|
+
|
|
75
|
+
### Product detail
|
|
76
|
+
|
|
77
|
+
Weekly order lines share the same vertical scale, so their heights are comparable. The lamp has one missing week, shown as a gap; its prepared total covers recorded weeks only. Expand **View values** to inspect the original samples.
|
|
78
|
+
|
|
79
|
+
:::table{data="./catalog/showcase-products.json" title="Performance by product"}
|
|
80
|
+
::column{field="image" label="Preview" kind="image" alt="image_alt"}
|
|
81
|
+
::column{field="product" label="Product"}
|
|
82
|
+
::column{field="weekly_orders" label="Weekly orders" kind="sparkline" sortBy="last_week"}
|
|
83
|
+
::column{field="orders" label="Orders" format="number"}
|
|
84
|
+
::column{field="revenue" label="Revenue" format="currency:USD"}
|
|
85
|
+
::column{field="change" label="Δ period" format="percent" sign="always" semantics="higher-is-better"}
|
|
86
|
+
:::
|
|
87
|
+
|
|
88
|
+
### Reading this table
|
|
89
|
+
|
|
90
|
+
- **Linen tote leads relative growth.** Comparisons are prepared by the author, not calculated from the sparkline.
|
|
91
|
+
- **Notebooks lead order volume.** They also have the highest final-week value in this fictional set.
|
|
92
|
+
- **The natural tote reuses its image.** The portable report embeds each unique illustration once.
|
|
93
|
+
|
|
94
|
+
:::callout{title="About these examples"}
|
|
95
|
+
All datasets, comparisons, statuses, findings and product illustrations are fictional. Store totals cover 1–7 November 2026; the onboarding and catalogue sections identify their separate periods and populations. Comparisons are prepared by the author, not inferred by the renderer. No live filters, data connections or subtotal calculations are implied.
|
|
96
|
+
:::
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plotleaf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Beautiful, interactive reports from Markdown and JSON. Built for agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,12 +26,17 @@
|
|
|
26
26
|
"dist/assets/styles.css",
|
|
27
27
|
"dist/assets/notices.txt",
|
|
28
28
|
"schema/components.json",
|
|
29
|
+
"examples/report.md",
|
|
29
30
|
"examples/storefront/*.md",
|
|
30
31
|
"examples/storefront/*.json",
|
|
31
32
|
"examples/adoption/*.md",
|
|
32
33
|
"examples/adoption/*.json",
|
|
33
34
|
"examples/onboarding/*.md",
|
|
34
35
|
"examples/onboarding/*.json",
|
|
36
|
+
"examples/catalog/*.md",
|
|
37
|
+
"examples/catalog/*.json",
|
|
38
|
+
"examples/catalog/assets/*.png",
|
|
39
|
+
"examples/catalog/assets/*.jpg",
|
|
35
40
|
"skills/plotleaf/SKILL.md",
|
|
36
41
|
"README.md",
|
|
37
42
|
"LICENSE",
|
|
@@ -46,10 +51,12 @@
|
|
|
46
51
|
"test:browser": "node node_modules/@playwright/test/cli.js test",
|
|
47
52
|
"test:consumer": "node tests/consumer/smoke.mjs",
|
|
48
53
|
"check": "bun run typecheck && bun run build && bun run test && bun run test:consumer",
|
|
49
|
-
"audit:package": "node scripts/audit-package.mjs"
|
|
54
|
+
"audit:package": "node scripts/audit-package.mjs",
|
|
55
|
+
"dev": "bun run build && node dist/cli.js dev examples/report.md"
|
|
50
56
|
},
|
|
51
57
|
"dependencies": {
|
|
52
58
|
"chokidar": "6.0.0",
|
|
59
|
+
"image-size": "2.1.0",
|
|
53
60
|
"open": "11.0.4",
|
|
54
61
|
"rehype-sanitize": "6.0.0",
|
|
55
62
|
"rehype-stringify": "10.0.1",
|
package/schema/components.json
CHANGED
|
@@ -12,10 +12,15 @@
|
|
|
12
12
|
"donutSlices": 8,
|
|
13
13
|
"funnelStages": 20,
|
|
14
14
|
"tableRows": 1000,
|
|
15
|
-
"tableColumns": 40
|
|
15
|
+
"tableColumns": 40,
|
|
16
|
+
"sparklinePoints": 64,
|
|
17
|
+
"imageFiles": 64,
|
|
18
|
+
"imageBytes": 1048576,
|
|
19
|
+
"imageDimension": 4096,
|
|
20
|
+
"imagePixels": 4000000
|
|
16
21
|
},
|
|
17
22
|
"data": {
|
|
18
|
-
"shape": "array of flat JSON records",
|
|
23
|
+
"shape": "array of flat JSON records; explicit table sparkline columns permit bounded numeric/null arrays only in their declared fields",
|
|
19
24
|
"resolution": "relative to the report directory; local .json files only",
|
|
20
25
|
"percent": "ratios: 0.12 displays as 12%",
|
|
21
26
|
"percentagePoints": "point values: 0.8 displays as 0.8 pt",
|
|
@@ -451,7 +456,7 @@
|
|
|
451
456
|
"example": ":::table{data=\"./plans.json\" title=\"By plan\"}\n::column{field=\"plan\" label=\"Plan\"}\n::column{field=\"change\" label=\"Δ QoQ\" format=\"percent\" sign=\"always\"}\n:::"
|
|
452
457
|
},
|
|
453
458
|
"column": {
|
|
454
|
-
"description": "
|
|
459
|
+
"description": "A scalar, local image or bounded sparkline column. Omitted kind means scalar.",
|
|
455
460
|
"forms": [
|
|
456
461
|
"leaf"
|
|
457
462
|
],
|
|
@@ -470,6 +475,24 @@
|
|
|
470
475
|
"minLength": 1,
|
|
471
476
|
"maxLength": 1000
|
|
472
477
|
},
|
|
478
|
+
"kind": {
|
|
479
|
+
"type": "string",
|
|
480
|
+
"enum": [
|
|
481
|
+
"scalar",
|
|
482
|
+
"image",
|
|
483
|
+
"sparkline"
|
|
484
|
+
]
|
|
485
|
+
},
|
|
486
|
+
"alt": {
|
|
487
|
+
"type": "string",
|
|
488
|
+
"minLength": 1,
|
|
489
|
+
"maxLength": 1000
|
|
490
|
+
},
|
|
491
|
+
"sortBy": {
|
|
492
|
+
"type": "string",
|
|
493
|
+
"minLength": 1,
|
|
494
|
+
"maxLength": 1000
|
|
495
|
+
},
|
|
473
496
|
"format": {
|
|
474
497
|
"type": "string",
|
|
475
498
|
"pattern": "^(number|percent|percentage-points|currency:[A-Z]{3})$"
|
|
@@ -499,7 +522,12 @@
|
|
|
499
522
|
],
|
|
500
523
|
"additionalProperties": false
|
|
501
524
|
},
|
|
502
|
-
"example": "::column{field=\"change\" label=\"Δ QoQ\" format=\"percent\" sign=\"always\" semantics=\"higher-is-better\"}"
|
|
525
|
+
"example": "::column{field=\"change\" label=\"Δ QoQ\" format=\"percent\" sign=\"always\" semantics=\"higher-is-better\"}",
|
|
526
|
+
"dataRules": {
|
|
527
|
+
"image": "Local static PNG/JPEG path or null. alt names a nonblank string field for each image. Fixed 64 × 48 display box; deduplicated and embedded offline. Paths resolve from the report directory.",
|
|
528
|
+
"sparkline": "Only explicitly declared sparkline fields may contain arrays: at most 64 finite numbers/nulls, in sample order. Nulls are gaps. Shared observed y scale per column; no aggregation or resampling.",
|
|
529
|
+
"sortBy": "Optional scalar field for image/sparkline sorting. Without it a rich column has a plain header. Scalar columns sort their own raw values. Sort keys are readable in the export."
|
|
530
|
+
}
|
|
503
531
|
},
|
|
504
532
|
"callout": {
|
|
505
533
|
"description": "A contextual note or warning containing ordinary Markdown.",
|
|
@@ -546,6 +574,7 @@
|
|
|
546
574
|
"Line/area null y values create gaps; bar null y values are invalid.",
|
|
547
575
|
"Donut category/value fields must differ; category count is bounded, and all-zero values do not create equal slices.",
|
|
548
576
|
"Funnel fields must differ; shares and denominators are supplied, validated and displayed per row, with no sorting or status inference.",
|
|
549
|
-
"
|
|
577
|
+
"Only explicit image columns can load local static PNG/JPEG; every resource stays within the report root and shared byte limits.",
|
|
578
|
+
"Filters, maps, arbitrary nested data and executable components are not supported in this release."
|
|
550
579
|
]
|
|
551
580
|
}
|
package/skills/plotleaf/SKILL.md
CHANGED
|
@@ -11,9 +11,9 @@ Use an installed `plotleaf` CLI or `npx plotleaf`. In an unpublished source chec
|
|
|
11
11
|
|
|
12
12
|
## Discover the contract
|
|
13
13
|
|
|
14
|
-
Run `plotleaf schema` (or `npx plotleaf schema`) for the current machine-readable component/prop specifications, rules and input limits. Prefer that output over remembered syntax. Supported components
|
|
14
|
+
Run `plotleaf schema` (or `npx plotleaf schema`) for the current machine-readable component/prop specifications, rules and input limits. Prefer that output over remembered syntax. Supported components include `grid`, `metric`, `line`, `area`, `bar`, `donut`, `funnel`, `table`, `column` and `callout`. Donut and funnel require plotleaf 0.2.0 or newer; check the installed CLI's schema when using an older version.
|
|
15
15
|
|
|
16
|
-
Use ordinary Markdown for headings, prose, findings, emphasis, lists, links and dividers. Headings provide sections. Design is built in; do not add custom CSS, raw HTML or framework components. Filters, maps,
|
|
16
|
+
Use ordinary Markdown for headings, prose, findings, emphasis, lists, links and dividers. Headings provide sections. Design is built in; do not add custom CSS, raw HTML or framework components. Image/sparkline column kinds require 0.3.0 or newer; check the installed CLI version and schema. Filters, maps, subtotals and executable/custom components remain future capabilities.
|
|
17
17
|
|
|
18
18
|
## Author prepared content
|
|
19
19
|
|
|
@@ -43,10 +43,10 @@ An overview of the prepared dataset, its period and relevant findings.
|
|
|
43
43
|
|
|
44
44
|
Literal attributes are quoted strings, decoded by the component schema. `::` is a leaf; `:::` encloses a container. Close every container explicitly. Use a longer outer fence (`::::grid`) when placing another container, such as a table, inside it. Put components at the document root or in allowed component containers, not inline or inside lists/blockquotes. `column` belongs only inside a table. A plain `::table{data="./file.json"}` infers all fields; explicit columns select/order/format fields and exclude unused columns from export.
|
|
45
45
|
|
|
46
|
-
- Datasets are JSON arrays of flat scalar records. Paths resolve from the Markdown file's directory. Keep them there or in subdirectories; remote, hidden, absolute and outside-root/symlink paths are rejected.
|
|
46
|
+
- Datasets are JSON arrays of flat scalar records, except explicitly declared table sparkline fields. Paths resolve from the Markdown file's directory. Keep them there or in subdirectories; remote, hidden, absolute and outside-root/symlink paths are rejected.
|
|
47
47
|
- Chart fields are literal keys. Charts do not aggregate, join or sort. Prepare a distinct category per bar; date/number x values must be unique and increasing. `xType="date"` accepts real `YYYY-MM-DD` dates or timestamps with an explicit zone. Dates are displayed in UTC.
|
|
48
48
|
- y values are finite JSON numbers. Line/area nulls create gaps; bar nulls are invalid. Missing fields are errors. Empty arrays produce an explicit no-data state and a warning.
|
|
49
|
-
- Table records have consistent keys and non-null types per column. Scalar values stay text;
|
|
49
|
+
- Table records have consistent keys and non-null types per column. Scalar values stay text; rich cells use explicit column kinds, never HTML templates. Sorting uses raw values. Use a smaller prepared summary if validation reports table/point limits; the engine never truncates silently.
|
|
50
50
|
- Numeric formats are `number`, `percent`, `percentage-points` and `currency:GBP` (validated ISO code). Percent uses ratios (`0.12` → 12%); percentage-points uses point values (`0.8` → 0.8 pt); currency uses major units. Whole currency amounts omit `.00`; nonzero cents retain two decimal places. Formatting uses `en-GB`.
|
|
51
51
|
- Metrics, comparisons and business sentiment are supplied by the agent. An increase is not automatically positive. Comparison direction and sentiment are independent; both require comparison text when explicitly supplied. Numeric table changes can declare `higher-is-better` or `lower-is-better` semantics.
|
|
52
52
|
- Include data period, provenance and limitations in the prose where they help readers assess the report. Do not invent findings or imply filters/refresh that the file does not provide.
|
|
@@ -63,6 +63,23 @@ Literal attributes are quoted strings, decoded by the component schema. `::` is
|
|
|
63
63
|
- Funnel stage names are unique nonblank strings, with at most 20 stages in source order. Each stage can have its own denominator; explain that choice in prose. Status values are `on-target`, `below-target`, `at-risk` or `neutral`. Omit the status mapping for neutral styling; the engine never infers business status. All mapped fields must differ.
|
|
64
64
|
- Use the full-value data disclosures to check counts behind compact funnel labels. Both components retain readable HTML data without JavaScript. Keep the denominator of any nearby conversion KPI explicit when it differs from the funnel baseline.
|
|
65
65
|
|
|
66
|
+
## Rich tables (0.3.0 or newer)
|
|
67
|
+
|
|
68
|
+
```markdown
|
|
69
|
+
:::table{data="./products.json" title="Products"}
|
|
70
|
+
::column{field="image" kind="image" alt="description" label="Preview"}
|
|
71
|
+
::column{field="product" label="Product"}
|
|
72
|
+
::column{field="weekly_orders" kind="sparkline" sortBy="last_week" label="Weekly orders"}
|
|
73
|
+
:::
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- Omitted `kind` means scalar. Images require an `alt` field containing a nonblank string for each non-null image; null image cells may have null alternatives. Image fields contain a local static PNG/JPEG path or null, resolved from the Markdown directory. Do not supply SVG, GIF/WebP, remote/data URLs or absolute/hidden/outside-root paths. The display box is 64 × 48; prepare images within the schema's byte/dimension/reference limits.
|
|
77
|
+
- Each unique image is embedded once. Its complete original bytes and metadata are readable by recipients; prepare assets for that disclosure. Alternatives are text, not markup. No image styling or numeric-format properties are supported.
|
|
78
|
+
- Sparkline fields contain at most 64 finite numbers/nulls or null. Only explicitly declared sparkline fields may contain sequences. Use a separate flat dataset for a chart or an inferred/scalar table. Nested objects/arrays and numeric strings are invalid; rows retain consistent keys.
|
|
79
|
+
- Sparkline x positions follow sample order. All rows in the same column share its observed min/max y scale. Nulls are gaps; last-sample labels remain missing when the final sample is null. Empty/all-null series show no invented values. Use `format` for numeric labels; sign/sentiment settings apply only to scalar columns.
|
|
80
|
+
- Rich columns are unsortable unless `sortBy` names a separate scalar field with consistent non-null types. Prepare any sort key explicitly, such as `last_week`; it is readable in the export. The engine does not compare arrays or derive a total/trend. Scalar columns keep their existing raw-value sort.
|
|
81
|
+
- Check image alternatives, line gaps/shared scale, keyboard value disclosures, row sorting, both themes and narrow table scrolling. Images/sparklines remain visible without JavaScript. The fictional catalogue example demonstrates this syntax.
|
|
82
|
+
|
|
66
83
|
## Validate, preview and export
|
|
67
84
|
|
|
68
85
|
```bash
|
|
@@ -71,7 +88,7 @@ npx plotleaf dev report.md --no-open
|
|
|
71
88
|
npx plotleaf build report.md --out ./report.html
|
|
72
89
|
```
|
|
73
90
|
|
|
74
|
-
Repair diagnostics using their code, one-based source location, message and hint, then validate again. Preview watches Markdown and
|
|
91
|
+
Repair diagnostics using their code, one-based source location, message and hint, then validate again. Preview watches Markdown, referenced JSON and local table images; an invalid edit displays diagnostics and recovers after repair. Omit `--no-open` to open a browser. No backend/account is required.
|
|
75
92
|
|
|
76
93
|
Export is one self-contained interactive HTML file. Check it after copying it away from sources and disabling networking. Charts require JavaScript; prose, metrics, tables and native chart-data disclosures remain readable without it. All displayed/embedded values are readable by recipients: collapsing a table or hiding a field visually is not redaction. Prepare sensitive data before rendering; explicit-column tables and chart field projection help avoid exporting unused fields.
|
|
77
94
|
|