@miao-vision/cli 0.1.6 → 0.1.7
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 +16 -2
- package/dist/cli.cjs +980 -43
- package/dist/examples/finance-review-deck.yaml +110 -0
- package/dist/examples/finance-review.csv +7 -0
- package/dist/examples/ops-update-deck.yaml +106 -0
- package/dist/examples/ops-update.csv +7 -0
- package/dist/examples/product-metrics-deck.yaml +110 -0
- package/dist/examples/product-metrics.csv +7 -0
- package/dist/types.ts +22 -0
- package/examples/finance-review-deck.yaml +110 -0
- package/examples/finance-review.csv +7 -0
- package/examples/ops-update-deck.yaml +106 -0
- package/examples/ops-update.csv +7 -0
- package/examples/product-metrics-deck.yaml +110 -0
- package/examples/product-metrics.csv +7 -0
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -39673,7 +39673,7 @@ var require_xlsx = __commonJS({
|
|
|
39673
39673
|
if (DBF_SUPPORTED_VERSIONS.indexOf(n[0]) > -1 && n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o);
|
|
39674
39674
|
return read_prn(data, d, o, str);
|
|
39675
39675
|
}
|
|
39676
|
-
function
|
|
39676
|
+
function readFileSync4(filename, opts) {
|
|
39677
39677
|
var o = opts || {};
|
|
39678
39678
|
o.type = "file";
|
|
39679
39679
|
return readSync(filename, o);
|
|
@@ -40509,8 +40509,8 @@ var require_xlsx = __commonJS({
|
|
|
40509
40509
|
if (typeof parse_xlscfb !== "undefined") XLSX3.parse_xlscfb = parse_xlscfb;
|
|
40510
40510
|
XLSX3.parse_zip = parse_zip;
|
|
40511
40511
|
XLSX3.read = readSync;
|
|
40512
|
-
XLSX3.readFile =
|
|
40513
|
-
XLSX3.readFileSync =
|
|
40512
|
+
XLSX3.readFile = readFileSync4;
|
|
40513
|
+
XLSX3.readFileSync = readFileSync4;
|
|
40514
40514
|
XLSX3.write = writeSync;
|
|
40515
40515
|
XLSX3.writeFile = writeFileSync2;
|
|
40516
40516
|
XLSX3.writeFileSync = writeFileSync2;
|
|
@@ -40541,8 +40541,8 @@ var require_xlsx = __commonJS({
|
|
|
40541
40541
|
});
|
|
40542
40542
|
|
|
40543
40543
|
// src/cli.ts
|
|
40544
|
-
var
|
|
40545
|
-
var
|
|
40544
|
+
var import_node_fs3 = require("node:fs");
|
|
40545
|
+
var import_node_path3 = require("node:path");
|
|
40546
40546
|
var YAML = __toESM(require_dist(), 1);
|
|
40547
40547
|
|
|
40548
40548
|
// src/data-loader.ts
|
|
@@ -41267,16 +41267,17 @@ var DEFAULT_SVG_THEME = {
|
|
|
41267
41267
|
axisColor: "#94a3b8",
|
|
41268
41268
|
labelColor: "#475569"
|
|
41269
41269
|
};
|
|
41270
|
-
function renderChartSvg(chart, rows, svgTheme) {
|
|
41270
|
+
function renderChartSvg(chart, rows, svgTheme, options = {}) {
|
|
41271
41271
|
const theme = svgTheme ?? DEFAULT_SVG_THEME;
|
|
41272
41272
|
const data = prepareChartData(rows, chart);
|
|
41273
|
-
if (chart.type === "line" || chart.type === "area") return renderLineChart(chart, data, theme);
|
|
41274
|
-
if (chart.type === "bar") return renderBarChart(chart, data, theme);
|
|
41275
|
-
if (chart.type === "
|
|
41273
|
+
if (chart.type === "line" || chart.type === "area") return renderLineChart(chart, data, theme, options);
|
|
41274
|
+
if (chart.type === "bar") return renderBarChart(chart, data, theme, options);
|
|
41275
|
+
if (chart.type === "pie") return renderPieChart(chart, data, theme, options);
|
|
41276
|
+
if (chart.type === "table") return renderTable(chart, data, options);
|
|
41276
41277
|
if (chart.type === "bigvalue") return renderBigValue(chart, data);
|
|
41277
41278
|
return renderUnsupported(chart);
|
|
41278
41279
|
}
|
|
41279
|
-
function renderBarChart(chart, rows, theme) {
|
|
41280
|
+
function renderBarChart(chart, rows, theme, options) {
|
|
41280
41281
|
const xField = chart.encoding.x?.field ?? "";
|
|
41281
41282
|
const yField = chart.encoding.y?.field ?? "";
|
|
41282
41283
|
const width = numberStyle(chart, "width", 720);
|
|
@@ -41295,9 +41296,11 @@ function renderBarChart(chart, rows, theme) {
|
|
|
41295
41296
|
const x = margin.left + index * (barWidth + barGap);
|
|
41296
41297
|
const y = margin.top + chartHeight - barHeight;
|
|
41297
41298
|
const color = theme.palette[index % theme.palette.length];
|
|
41299
|
+
const label = String(row[xField] ?? "");
|
|
41300
|
+
const tooltip = `${label}: ${value}`;
|
|
41298
41301
|
return `<g>
|
|
41299
|
-
<rect x="${x.toFixed(1)}" y="${y.toFixed(1)}" width="${barWidth.toFixed(1)}" height="${barHeight.toFixed(1)}" rx="3" fill="${color}" />
|
|
41300
|
-
<text x="${(x + barWidth / 2).toFixed(1)}" y="${(margin.top + chartHeight + 18).toFixed(1)}" text-anchor="middle" fill="${theme.labelColor}" font-size="11">${escapeHtml(
|
|
41302
|
+
<rect ${markAttrs(options.chartId, xField, row[xField], index, tooltip)} x="${x.toFixed(1)}" y="${y.toFixed(1)}" width="${barWidth.toFixed(1)}" height="${barHeight.toFixed(1)}" rx="3" fill="${color}" />
|
|
41303
|
+
<text x="${(x + barWidth / 2).toFixed(1)}" y="${(margin.top + chartHeight + 18).toFixed(1)}" text-anchor="middle" fill="${theme.labelColor}" font-size="11">${escapeHtml(label)}</text>
|
|
41301
41304
|
</g>`;
|
|
41302
41305
|
}).join("");
|
|
41303
41306
|
return svgFrame(width, height, theme.background, `
|
|
@@ -41305,7 +41308,7 @@ function renderBarChart(chart, rows, theme) {
|
|
|
41305
41308
|
${bars}
|
|
41306
41309
|
`);
|
|
41307
41310
|
}
|
|
41308
|
-
function renderLineChart(chart, rows, theme) {
|
|
41311
|
+
function renderLineChart(chart, rows, theme, options) {
|
|
41309
41312
|
const xField = chart.encoding.x?.field ?? "";
|
|
41310
41313
|
const yField = chart.encoding.y?.field ?? "";
|
|
41311
41314
|
const width = numberStyle(chart, "width", 720);
|
|
@@ -41325,7 +41328,7 @@ function renderLineChart(chart, rows, theme) {
|
|
|
41325
41328
|
});
|
|
41326
41329
|
const path = points.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(" ");
|
|
41327
41330
|
const dots = points.map(
|
|
41328
|
-
(p) => `<circle cx="${p.x.toFixed(1)}" cy="${p.y.toFixed(1)}" r="4" fill="${lineColor}"><title>${escapeHtml(p.label)}: ${p.value}</title></circle>`
|
|
41331
|
+
(p) => `<circle ${markAttrs(options.chartId, xField, p.label, 0, `${p.label}: ${p.value}`)} cx="${p.x.toFixed(1)}" cy="${p.y.toFixed(1)}" r="4" fill="${lineColor}"><title>${escapeHtml(p.label)}: ${p.value}</title></circle>`
|
|
41329
41332
|
).join("");
|
|
41330
41333
|
const labels = points.map((p, i) => {
|
|
41331
41334
|
if (i % Math.ceil(points.length / 8) !== 0) return "";
|
|
@@ -41338,11 +41341,42 @@ function renderLineChart(chart, rows, theme) {
|
|
|
41338
41341
|
${labels}
|
|
41339
41342
|
`);
|
|
41340
41343
|
}
|
|
41341
|
-
function
|
|
41344
|
+
function renderPieChart(chart, rows, theme, options) {
|
|
41345
|
+
const labelField = chart.encoding.label?.field ?? "";
|
|
41346
|
+
const valueField = chart.encoding.value?.field ?? "";
|
|
41347
|
+
const width = numberStyle(chart, "width", 720);
|
|
41348
|
+
const height = numberStyle(chart, "height", 420);
|
|
41349
|
+
const cx = width / 2 - 80;
|
|
41350
|
+
const cy = height / 2;
|
|
41351
|
+
const radius = Math.min(width, height) * 0.34;
|
|
41352
|
+
const values = rows.map((row) => Math.max(0, Number(row[valueField]) || 0));
|
|
41353
|
+
const total = values.reduce((sum, value) => sum + value, 0) || 1;
|
|
41354
|
+
let angle = -Math.PI / 2;
|
|
41355
|
+
const slices = rows.map((row, index) => {
|
|
41356
|
+
const value = values[index];
|
|
41357
|
+
const nextAngle = angle + value / total * Math.PI * 2;
|
|
41358
|
+
const path = describeArc(cx, cy, radius, angle, nextAngle);
|
|
41359
|
+
const color = theme.palette[index % theme.palette.length];
|
|
41360
|
+
const label = String(row[labelField] ?? "");
|
|
41361
|
+
const tooltip = `${label}: ${value}`;
|
|
41362
|
+
angle = nextAngle;
|
|
41363
|
+
return `<path ${markAttrs(options.chartId, labelField, row[labelField], index, tooltip)} d="${path}" fill="${color}" stroke="${theme.background}" stroke-width="2" />`;
|
|
41364
|
+
}).join("");
|
|
41365
|
+
const legend = rows.map((row, index) => {
|
|
41366
|
+
const y = 72 + index * 24;
|
|
41367
|
+
return `<g>
|
|
41368
|
+
<rect x="${width - 210}" y="${y - 10}" width="10" height="10" fill="${theme.palette[index % theme.palette.length]}" />
|
|
41369
|
+
<text x="${width - 192}" y="${y}" fill="${theme.labelColor}" font-size="12">${escapeHtml(String(row[labelField] ?? ""))}</text>
|
|
41370
|
+
</g>`;
|
|
41371
|
+
}).join("");
|
|
41372
|
+
return svgFrame(width, height, theme.background, `${slices}${legend}`);
|
|
41373
|
+
}
|
|
41374
|
+
function renderTable(chart, rows, options) {
|
|
41342
41375
|
const columns = Object.keys(rows[0] ?? {}).slice(0, 8);
|
|
41343
41376
|
const header = columns.map((c) => `<th>${escapeHtml(c)}</th>`).join("");
|
|
41377
|
+
const markField = chart.encoding.label?.field ?? chart.encoding.x?.field ?? columns[0] ?? "";
|
|
41344
41378
|
const body = rows.slice(0, 20).map(
|
|
41345
|
-
(row) => `<tr>${columns.map((c) => `<td>${escapeHtml(String(row[c] ?? ""))}</td>`).join("")}</tr>`
|
|
41379
|
+
(row) => `<tr ${markAttrs(options.chartId, markField, row[markField], 0, String(row[markField] ?? "Row"))}>${columns.map((c) => `<td>${escapeHtml(String(row[c] ?? ""))}</td>`).join("")}</tr>`
|
|
41346
41380
|
).join("");
|
|
41347
41381
|
return `<div class="miao-table-wrap"><table class="miao-table"><caption>${escapeHtml(chart.title ?? "Table")}</caption><thead><tr>${header}</tr></thead><tbody>${body}</tbody></table></div>`;
|
|
41348
41382
|
}
|
|
@@ -41360,6 +41394,33 @@ function svgFrame(width, height, bgColor, body) {
|
|
|
41360
41394
|
${body}
|
|
41361
41395
|
</svg>`;
|
|
41362
41396
|
}
|
|
41397
|
+
function markAttrs(chartId, field, value, rowKey, tooltip) {
|
|
41398
|
+
return [
|
|
41399
|
+
'data-miao-mark="true"',
|
|
41400
|
+
chartId ? `data-chart-id="${escapeHtml(chartId)}"` : "",
|
|
41401
|
+
`data-field="${escapeHtml(field)}"`,
|
|
41402
|
+
`data-value="${escapeHtml(String(value ?? ""))}"`,
|
|
41403
|
+
`data-row-key="${escapeHtml(String(rowKey))}"`,
|
|
41404
|
+
`data-tooltip="${escapeHtml(tooltip)}"`
|
|
41405
|
+
].filter(Boolean).join(" ");
|
|
41406
|
+
}
|
|
41407
|
+
function describeArc(cx, cy, radius, startAngle, endAngle) {
|
|
41408
|
+
const start = polarToCartesian(cx, cy, radius, endAngle);
|
|
41409
|
+
const end = polarToCartesian(cx, cy, radius, startAngle);
|
|
41410
|
+
const largeArcFlag = endAngle - startAngle <= Math.PI ? "0" : "1";
|
|
41411
|
+
return [
|
|
41412
|
+
`M ${cx.toFixed(1)} ${cy.toFixed(1)}`,
|
|
41413
|
+
`L ${start.x.toFixed(1)} ${start.y.toFixed(1)}`,
|
|
41414
|
+
`A ${radius.toFixed(1)} ${radius.toFixed(1)} 0 ${largeArcFlag} 0 ${end.x.toFixed(1)} ${end.y.toFixed(1)}`,
|
|
41415
|
+
"Z"
|
|
41416
|
+
].join(" ");
|
|
41417
|
+
}
|
|
41418
|
+
function polarToCartesian(cx, cy, radius, angle) {
|
|
41419
|
+
return {
|
|
41420
|
+
x: cx + radius * Math.cos(angle),
|
|
41421
|
+
y: cy + radius * Math.sin(angle)
|
|
41422
|
+
};
|
|
41423
|
+
}
|
|
41363
41424
|
function buildAxis(margin, chartWidth, chartHeight, xLabel, yLabel, yMin, yMax, theme) {
|
|
41364
41425
|
const x0 = margin.left;
|
|
41365
41426
|
const y0 = margin.top + chartHeight;
|
|
@@ -41604,6 +41665,441 @@ function getTheme(name) {
|
|
|
41604
41665
|
return THEMES.default;
|
|
41605
41666
|
}
|
|
41606
41667
|
|
|
41668
|
+
// src/interactive-runtime-assets.ts
|
|
41669
|
+
var INTERACTIVE_CSS = `
|
|
41670
|
+
.miao-interactive-controls { display: flex; flex-wrap: wrap; gap: 12px; align-items: end; margin: 0 0 24px; padding: 12px 0; border-top: 1px solid rgba(128,128,128,0.18); border-bottom: 1px solid rgba(128,128,128,0.18); }
|
|
41671
|
+
.miao-filter { display: grid; gap: 5px; font-size: 12px; }
|
|
41672
|
+
.miao-filter label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.56; }
|
|
41673
|
+
.miao-filter select, .miao-filter input { min-width: 140px; border: 1px solid rgba(128,128,128,0.28); border-radius: 4px; padding: 6px 8px; background: transparent; color: inherit; font: inherit; }
|
|
41674
|
+
.miao-filter-range { display: flex; gap: 6px; }
|
|
41675
|
+
.miao-reset { border: 1px solid rgba(128,128,128,0.28); border-radius: 4px; padding: 7px 10px; background: transparent; color: inherit; cursor: pointer; font: inherit; }
|
|
41676
|
+
.miao-reset:hover { background: rgba(128,128,128,0.08); }
|
|
41677
|
+
.miao-chart-svg [data-miao-mark] { cursor: pointer; transition: opacity 0.15s ease, stroke-width 0.15s ease; }
|
|
41678
|
+
.miao-chart-svg [data-miao-mark]:hover { opacity: 0.78; }
|
|
41679
|
+
.miao-chart-svg [data-miao-selected="true"] { stroke: currentColor; stroke-width: 2; }
|
|
41680
|
+
.miao-mark-hidden { opacity: 0.18; }
|
|
41681
|
+
.miao-detail { margin-top: 12px; overflow: auto; max-height: 320px; border: 1px solid rgba(128,128,128,0.18); border-radius: 4px; }
|
|
41682
|
+
.miao-detail-title { padding: 9px 10px; font-size: 12px; font-weight: 700; border-bottom: 1px solid rgba(128,128,128,0.18); }
|
|
41683
|
+
.miao-detail table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
41684
|
+
.miao-detail th, .miao-detail td { padding: 7px 9px; border-bottom: 1px solid rgba(128,128,128,0.12); text-align: left; white-space: nowrap; }
|
|
41685
|
+
.miao-detail th { font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.64; }
|
|
41686
|
+
.miao-tooltip { position: fixed; z-index: 9999; pointer-events: none; padding: 6px 8px; border-radius: 4px; background: rgba(20,20,19,0.92); color: #fff; font: 12px/1.35 system-ui, sans-serif; box-shadow: 0 8px 24px rgba(0,0,0,0.18); transform: translate(10px, 10px); }
|
|
41687
|
+
`;
|
|
41688
|
+
var INTERACTIVE_JS = `
|
|
41689
|
+
(function() {
|
|
41690
|
+
var specEl = document.getElementById('miao-viz-spec');
|
|
41691
|
+
var dataEl = document.getElementById('miao-viz-data');
|
|
41692
|
+
if (!specEl || !dataEl) return;
|
|
41693
|
+
|
|
41694
|
+
var spec = JSON.parse(specEl.textContent || '{}');
|
|
41695
|
+
var rows = JSON.parse(dataEl.textContent || '[]');
|
|
41696
|
+
var filters = (spec.interactions && spec.interactions.globalFilters) || [];
|
|
41697
|
+
var state = { filters: {}, selection: null };
|
|
41698
|
+
var tooltip = createTooltip();
|
|
41699
|
+
|
|
41700
|
+
function init() {
|
|
41701
|
+
renderControls();
|
|
41702
|
+
update();
|
|
41703
|
+
}
|
|
41704
|
+
|
|
41705
|
+
function renderControls() {
|
|
41706
|
+
if (!filters.length) return;
|
|
41707
|
+
var main = document.querySelector('.miao-viz-report');
|
|
41708
|
+
if (!main) return;
|
|
41709
|
+
var controls = document.createElement('section');
|
|
41710
|
+
controls.className = 'miao-interactive-controls';
|
|
41711
|
+
controls.setAttribute('aria-label', 'Interactive filters');
|
|
41712
|
+
filters.forEach(function(filter) {
|
|
41713
|
+
controls.appendChild(filter.type === 'range' ? renderRangeFilter(filter) : renderSelectFilter(filter));
|
|
41714
|
+
});
|
|
41715
|
+
var reset = document.createElement('button');
|
|
41716
|
+
reset.type = 'button';
|
|
41717
|
+
reset.className = 'miao-reset';
|
|
41718
|
+
reset.textContent = 'Reset';
|
|
41719
|
+
reset.addEventListener('click', function() {
|
|
41720
|
+
state.filters = {};
|
|
41721
|
+
state.selection = null;
|
|
41722
|
+
controls.querySelectorAll('select,input').forEach(function(input) { input.value = ''; });
|
|
41723
|
+
update();
|
|
41724
|
+
});
|
|
41725
|
+
controls.appendChild(reset);
|
|
41726
|
+
var header = main.querySelector('header');
|
|
41727
|
+
main.insertBefore(controls, header ? header.nextSibling : main.firstChild);
|
|
41728
|
+
}
|
|
41729
|
+
|
|
41730
|
+
function renderSelectFilter(filter) {
|
|
41731
|
+
var wrap = document.createElement('div');
|
|
41732
|
+
wrap.className = 'miao-filter';
|
|
41733
|
+
var label = document.createElement('label');
|
|
41734
|
+
label.textContent = filter.field;
|
|
41735
|
+
var select = document.createElement('select');
|
|
41736
|
+
select.innerHTML = '<option value="">All</option>' + uniqueValues(filter.field).map(function(value) {
|
|
41737
|
+
return '<option value="' + escapeAttr(value) + '">' + escapeHtml(value) + '</option>';
|
|
41738
|
+
}).join('');
|
|
41739
|
+
select.addEventListener('change', function() {
|
|
41740
|
+
state.filters[filter.field] = select.value;
|
|
41741
|
+
update();
|
|
41742
|
+
});
|
|
41743
|
+
wrap.appendChild(label);
|
|
41744
|
+
wrap.appendChild(select);
|
|
41745
|
+
return wrap;
|
|
41746
|
+
}
|
|
41747
|
+
|
|
41748
|
+
function renderRangeFilter(filter) {
|
|
41749
|
+
var wrap = document.createElement('div');
|
|
41750
|
+
wrap.className = 'miao-filter';
|
|
41751
|
+
var label = document.createElement('label');
|
|
41752
|
+
label.textContent = filter.field;
|
|
41753
|
+
var pair = document.createElement('div');
|
|
41754
|
+
pair.className = 'miao-filter-range';
|
|
41755
|
+
var min = document.createElement('input');
|
|
41756
|
+
var max = document.createElement('input');
|
|
41757
|
+
min.placeholder = 'Min';
|
|
41758
|
+
max.placeholder = 'Max';
|
|
41759
|
+
[min, max].forEach(function(input) {
|
|
41760
|
+
input.addEventListener('input', function() {
|
|
41761
|
+
state.filters[filter.field] = [min.value, max.value];
|
|
41762
|
+
update();
|
|
41763
|
+
});
|
|
41764
|
+
});
|
|
41765
|
+
pair.appendChild(min);
|
|
41766
|
+
pair.appendChild(max);
|
|
41767
|
+
wrap.appendChild(label);
|
|
41768
|
+
wrap.appendChild(pair);
|
|
41769
|
+
return wrap;
|
|
41770
|
+
}
|
|
41771
|
+
|
|
41772
|
+
function bindMarks() {
|
|
41773
|
+
document.querySelectorAll('[data-miao-mark]').forEach(function(mark) {
|
|
41774
|
+
var chart = chartSpec(mark.getAttribute('data-chart-id'));
|
|
41775
|
+
mark.addEventListener('mouseenter', function(event) {
|
|
41776
|
+
var text = mark.getAttribute('data-tooltip');
|
|
41777
|
+
if (!text) return;
|
|
41778
|
+
tooltip.textContent = text;
|
|
41779
|
+
tooltip.hidden = false;
|
|
41780
|
+
moveTooltip(event);
|
|
41781
|
+
});
|
|
41782
|
+
mark.addEventListener('mousemove', moveTooltip);
|
|
41783
|
+
mark.addEventListener('mouseleave', function() { tooltip.hidden = true; });
|
|
41784
|
+
mark.addEventListener('click', function() {
|
|
41785
|
+
if (!canSelect(chart)) return;
|
|
41786
|
+
var field = mark.getAttribute('data-field');
|
|
41787
|
+
var value = mark.getAttribute('data-value');
|
|
41788
|
+
if (!field) return;
|
|
41789
|
+
state.selection = state.selection && state.selection.field === field && String(state.selection.value) === String(value)
|
|
41790
|
+
? null
|
|
41791
|
+
: { field: field, value: value };
|
|
41792
|
+
update();
|
|
41793
|
+
});
|
|
41794
|
+
});
|
|
41795
|
+
}
|
|
41796
|
+
|
|
41797
|
+
function update() {
|
|
41798
|
+
var filtered = applyFilters(rows);
|
|
41799
|
+
document.querySelectorAll('[data-miao-chart]').forEach(function(container) {
|
|
41800
|
+
var chart = chartSpec(container.getAttribute('data-miao-chart'));
|
|
41801
|
+
renderChart(container, chart, filtered);
|
|
41802
|
+
});
|
|
41803
|
+
bindMarks();
|
|
41804
|
+
document.querySelectorAll('[data-miao-mark]').forEach(function(mark) {
|
|
41805
|
+
var field = mark.getAttribute('data-field');
|
|
41806
|
+
var value = mark.getAttribute('data-value');
|
|
41807
|
+
var selected = state.selection && state.selection.field === field && String(state.selection.value) === String(value);
|
|
41808
|
+
mark.setAttribute('data-miao-selected', selected ? 'true' : 'false');
|
|
41809
|
+
mark.classList.toggle('miao-mark-hidden', Boolean((state.selection && !selected) || !markMatchesFilters(field, value)));
|
|
41810
|
+
});
|
|
41811
|
+
document.querySelectorAll('[data-miao-chart]').forEach(function(chart) {
|
|
41812
|
+
renderDetail(chart, filtered);
|
|
41813
|
+
});
|
|
41814
|
+
}
|
|
41815
|
+
|
|
41816
|
+
function renderChart(container, chart, sourceRows) {
|
|
41817
|
+
if (!chart || ['bar', 'pie', 'table'].indexOf(chart.type) === -1) return;
|
|
41818
|
+
var slot = container.querySelector('.miao-render-slot');
|
|
41819
|
+
if (!slot) return;
|
|
41820
|
+
var chartRows = prepareRows(sourceRows, chart);
|
|
41821
|
+
if (chart.type === 'bar') slot.innerHTML = renderBar(chart, chartRows, container.getAttribute('data-miao-chart'));
|
|
41822
|
+
else if (chart.type === 'pie') slot.innerHTML = renderPie(chart, chartRows, container.getAttribute('data-miao-chart'));
|
|
41823
|
+
else if (chart.type === 'table') slot.innerHTML = renderTable(chart, chartRows, container.getAttribute('data-miao-chart'));
|
|
41824
|
+
}
|
|
41825
|
+
|
|
41826
|
+
function prepareRows(sourceRows, chart) {
|
|
41827
|
+
return ((chart.data && chart.data.transform) || []).reduce(function(current, transform) {
|
|
41828
|
+
if (transform.type === 'derive-month' && transform.field && transform.as) {
|
|
41829
|
+
return current.map(function(row) {
|
|
41830
|
+
var copy = Object.assign({}, row);
|
|
41831
|
+
copy[transform.as] = toMonth(row[transform.field]);
|
|
41832
|
+
return copy;
|
|
41833
|
+
});
|
|
41834
|
+
}
|
|
41835
|
+
if (transform.type === 'aggregate') return aggregateRows(current, transform);
|
|
41836
|
+
if (transform.type === 'sort' && transform.field) {
|
|
41837
|
+
var order = transform.order === 'asc' ? 1 : -1;
|
|
41838
|
+
return current.slice().sort(function(a, b) {
|
|
41839
|
+
var an = Number(a[transform.field]);
|
|
41840
|
+
var bn = Number(b[transform.field]);
|
|
41841
|
+
if (Number.isFinite(an) && Number.isFinite(bn)) return (an - bn) * order;
|
|
41842
|
+
return String(a[transform.field] || '').localeCompare(String(b[transform.field] || '')) * order;
|
|
41843
|
+
});
|
|
41844
|
+
}
|
|
41845
|
+
if (transform.type === 'limit' && typeof transform.value === 'number') return current.slice(0, transform.value);
|
|
41846
|
+
return current;
|
|
41847
|
+
}, sourceRows.slice());
|
|
41848
|
+
}
|
|
41849
|
+
|
|
41850
|
+
function aggregateRows(sourceRows, transform) {
|
|
41851
|
+
var groupBy = transform.groupBy || [];
|
|
41852
|
+
var measures = transform.measures || [];
|
|
41853
|
+
var groups = new Map();
|
|
41854
|
+
sourceRows.forEach(function(row) {
|
|
41855
|
+
var key = JSON.stringify(groupBy.map(function(field) { return row[field]; }));
|
|
41856
|
+
var existing = groups.get(key) || [];
|
|
41857
|
+
existing.push(row);
|
|
41858
|
+
groups.set(key, existing);
|
|
41859
|
+
});
|
|
41860
|
+
return Array.from(groups.values()).map(function(groupRows) {
|
|
41861
|
+
var first = groupRows[0] || {};
|
|
41862
|
+
var out = {};
|
|
41863
|
+
groupBy.forEach(function(field) { out[field] = first[field]; });
|
|
41864
|
+
measures.forEach(function(measure) { out[measure.as] = aggregateMeasure(groupRows, measure); });
|
|
41865
|
+
return out;
|
|
41866
|
+
});
|
|
41867
|
+
}
|
|
41868
|
+
|
|
41869
|
+
function aggregateMeasure(sourceRows, measure) {
|
|
41870
|
+
if (measure.op === 'count') return sourceRows.length;
|
|
41871
|
+
var values = sourceRows.map(function(row) { return Number(row[measure.field]); }).filter(Number.isFinite);
|
|
41872
|
+
if (!values.length) return 0;
|
|
41873
|
+
if (measure.op === 'avg') return values.reduce(sum, 0) / values.length;
|
|
41874
|
+
if (measure.op === 'min') return Math.min.apply(null, values);
|
|
41875
|
+
if (measure.op === 'max') return Math.max.apply(null, values);
|
|
41876
|
+
return values.reduce(sum, 0);
|
|
41877
|
+
}
|
|
41878
|
+
|
|
41879
|
+
function renderBar(chart, chartRows, chartId) {
|
|
41880
|
+
var xField = field(chart, 'x');
|
|
41881
|
+
var yField = field(chart, 'y');
|
|
41882
|
+
var width = numberStyle(chart, 'width', 720);
|
|
41883
|
+
var height = numberStyle(chart, 'height', 420);
|
|
41884
|
+
var margin = { top: 24, right: 24, bottom: 48, left: 72 };
|
|
41885
|
+
var chartWidth = width - margin.left - margin.right;
|
|
41886
|
+
var chartHeight = height - margin.top - margin.bottom;
|
|
41887
|
+
var values = chartRows.map(function(row) { return Number(row[yField]); }).filter(Number.isFinite);
|
|
41888
|
+
var yMax = Math.max.apply(null, values.concat([1]));
|
|
41889
|
+
var gap = 8;
|
|
41890
|
+
var barWidth = Math.max(8, (chartWidth - gap * Math.max(chartRows.length - 1, 0)) / Math.max(chartRows.length, 1));
|
|
41891
|
+
var body = chartRows.map(function(row, index) {
|
|
41892
|
+
var value = Number(row[yField]) || 0;
|
|
41893
|
+
var barHeight = value / yMax * chartHeight;
|
|
41894
|
+
var x = margin.left + index * (barWidth + gap);
|
|
41895
|
+
var y = margin.top + chartHeight - barHeight;
|
|
41896
|
+
var label = String(row[xField] == null ? '' : row[xField]);
|
|
41897
|
+
return '<g><rect ' + markAttrs(chartId, xField, row[xField], index, label + ': ' + value) +
|
|
41898
|
+
' x="' + fixed(x) + '" y="' + fixed(y) + '" width="' + fixed(barWidth) + '" height="' + fixed(barHeight) +
|
|
41899
|
+
'" rx="3" fill="' + color(index) + '" />' +
|
|
41900
|
+
'<text x="' + fixed(x + barWidth / 2) + '" y="' + fixed(margin.top + chartHeight + 18) +
|
|
41901
|
+
'" text-anchor="middle" fill="#475569" font-size="11">' + escapeHtml(label) + '</text></g>';
|
|
41902
|
+
}).join('');
|
|
41903
|
+
return svgFrame(width, height, body);
|
|
41904
|
+
}
|
|
41905
|
+
|
|
41906
|
+
function renderPie(chart, chartRows, chartId) {
|
|
41907
|
+
var labelField = field(chart, 'label');
|
|
41908
|
+
var valueField = field(chart, 'value');
|
|
41909
|
+
var width = numberStyle(chart, 'width', 720);
|
|
41910
|
+
var height = numberStyle(chart, 'height', 420);
|
|
41911
|
+
var cx = width / 2 - 80;
|
|
41912
|
+
var cy = height / 2;
|
|
41913
|
+
var radius = Math.min(width, height) * 0.34;
|
|
41914
|
+
var values = chartRows.map(function(row) { return Math.max(0, Number(row[valueField]) || 0); });
|
|
41915
|
+
var total = values.reduce(sum, 0) || 1;
|
|
41916
|
+
var angle = -Math.PI / 2;
|
|
41917
|
+
var slices = chartRows.map(function(row, index) {
|
|
41918
|
+
var value = values[index];
|
|
41919
|
+
var nextAngle = angle + value / total * Math.PI * 2;
|
|
41920
|
+
var path = describeArc(cx, cy, radius, angle, nextAngle);
|
|
41921
|
+
var label = String(row[labelField] == null ? '' : row[labelField]);
|
|
41922
|
+
angle = nextAngle;
|
|
41923
|
+
return '<path ' + markAttrs(chartId, labelField, row[labelField], index, label + ': ' + value) +
|
|
41924
|
+
' d="' + path + '" fill="' + color(index) + '" stroke="#fff" stroke-width="2" />';
|
|
41925
|
+
}).join('');
|
|
41926
|
+
return svgFrame(width, height, slices);
|
|
41927
|
+
}
|
|
41928
|
+
|
|
41929
|
+
function renderTable(chart, chartRows, chartId) {
|
|
41930
|
+
var columns = Object.keys(chartRows[0] || rows[0] || {}).slice(0, 8);
|
|
41931
|
+
var markField = field(chart, 'label') || field(chart, 'x') || columns[0] || '';
|
|
41932
|
+
return '<div class="miao-table-wrap"><table class="miao-table"><thead><tr>' +
|
|
41933
|
+
columns.map(function(col) { return '<th>' + escapeHtml(col) + '</th>'; }).join('') +
|
|
41934
|
+
'</tr></thead><tbody>' + chartRows.slice(0, 20).map(function(row, index) {
|
|
41935
|
+
return '<tr ' + markAttrs(chartId, markField, row[markField], index, String(row[markField] || 'Row')) + '>' +
|
|
41936
|
+
columns.map(function(col) { return '<td>' + escapeHtml(row[col] == null ? '' : row[col]) + '</td>'; }).join('') +
|
|
41937
|
+
'</tr>';
|
|
41938
|
+
}).join('') + '</tbody></table></div>';
|
|
41939
|
+
}
|
|
41940
|
+
|
|
41941
|
+
function renderDetail(chart, filtered) {
|
|
41942
|
+
var chartId = chart.getAttribute('data-miao-chart');
|
|
41943
|
+
var detail = chart.querySelector('.miao-detail');
|
|
41944
|
+
if (!detail) {
|
|
41945
|
+
detail = document.createElement('div');
|
|
41946
|
+
detail.className = 'miao-detail';
|
|
41947
|
+
chart.appendChild(detail);
|
|
41948
|
+
}
|
|
41949
|
+
if (!state.selection) {
|
|
41950
|
+
detail.innerHTML = '';
|
|
41951
|
+
detail.hidden = true;
|
|
41952
|
+
return;
|
|
41953
|
+
}
|
|
41954
|
+
var selectedRows = filtered.filter(function(row) {
|
|
41955
|
+
return String(row[state.selection.field] == null ? '' : row[state.selection.field]) === String(state.selection.value);
|
|
41956
|
+
});
|
|
41957
|
+
detail.hidden = false;
|
|
41958
|
+
detail.innerHTML = detailTable(selectedRows, chartId);
|
|
41959
|
+
}
|
|
41960
|
+
|
|
41961
|
+
function applyFilters(source) {
|
|
41962
|
+
return source.filter(function(row) {
|
|
41963
|
+
return filters.every(function(filter) {
|
|
41964
|
+
var active = state.filters[filter.field];
|
|
41965
|
+
if (active == null || active === '' || (Array.isArray(active) && !active[0] && !active[1])) return true;
|
|
41966
|
+
if (filter.type === 'select') return String(row[filter.field] == null ? '' : row[filter.field]) === String(active);
|
|
41967
|
+
var current = comparable(row[filter.field]);
|
|
41968
|
+
if (current == null) return false;
|
|
41969
|
+
var min = comparable(active[0]);
|
|
41970
|
+
var max = comparable(active[1]);
|
|
41971
|
+
if (min != null && current < min) return false;
|
|
41972
|
+
if (max != null && current > max) return false;
|
|
41973
|
+
return true;
|
|
41974
|
+
});
|
|
41975
|
+
});
|
|
41976
|
+
}
|
|
41977
|
+
|
|
41978
|
+
function markMatchesFilters(field, value) {
|
|
41979
|
+
var activeFilters = filters.filter(function(filter) { return filter.field === field; });
|
|
41980
|
+
if (!activeFilters.length) return true;
|
|
41981
|
+
return activeFilters.every(function(filter) {
|
|
41982
|
+
var active = state.filters[filter.field];
|
|
41983
|
+
if (active == null || active === '' || (Array.isArray(active) && !active[0] && !active[1])) return true;
|
|
41984
|
+
if (filter.type === 'select') return String(value == null ? '' : value) === String(active);
|
|
41985
|
+
var current = comparable(value);
|
|
41986
|
+
if (current == null) return false;
|
|
41987
|
+
var min = comparable(active[0]);
|
|
41988
|
+
var max = comparable(active[1]);
|
|
41989
|
+
if (min != null && current < min) return false;
|
|
41990
|
+
if (max != null && current > max) return false;
|
|
41991
|
+
return true;
|
|
41992
|
+
});
|
|
41993
|
+
}
|
|
41994
|
+
|
|
41995
|
+
function chartSpec(chartId) {
|
|
41996
|
+
return (spec.charts || []).find(function(chart, index) { return (chart.id || ('chart-' + (index + 1))) === chartId; }) || null;
|
|
41997
|
+
}
|
|
41998
|
+
function canSelect(chart) { return Boolean(chart && ((chart.interaction && chart.interaction.select) || chart.drilldownPreset)); }
|
|
41999
|
+
function field(chart, channel) { return chart && chart.encoding && chart.encoding[channel] ? chart.encoding[channel].field : ''; }
|
|
42000
|
+
function numberStyle(chart, key, fallback) { return chart && chart.style && typeof chart.style[key] === 'number' ? chart.style[key] : fallback; }
|
|
42001
|
+
|
|
42002
|
+
function svgFrame(width, height, body) {
|
|
42003
|
+
return '<svg class="miao-chart-svg" viewBox="0 0 ' + width + ' ' + height + '" width="100%" height="' + height +
|
|
42004
|
+
'" role="img" xmlns="http://www.w3.org/2000/svg"><rect x="0" y="0" width="' + width + '" height="' + height +
|
|
42005
|
+
'" fill="#fff" />' + body + '</svg>';
|
|
42006
|
+
}
|
|
42007
|
+
|
|
42008
|
+
function markAttrs(chartId, markField, value, rowKey, tooltipText) {
|
|
42009
|
+
return 'data-miao-mark="true" data-chart-id="' + escapeAttr(chartId || '') + '" data-field="' + escapeAttr(markField || '') +
|
|
42010
|
+
'" data-value="' + escapeAttr(value == null ? '' : value) + '" data-row-key="' + escapeAttr(rowKey) +
|
|
42011
|
+
'" data-tooltip="' + escapeAttr(tooltipText || '') + '"';
|
|
42012
|
+
}
|
|
42013
|
+
|
|
42014
|
+
function describeArc(cx, cy, radius, startAngle, endAngle) {
|
|
42015
|
+
var start = polarToCartesian(cx, cy, radius, endAngle);
|
|
42016
|
+
var end = polarToCartesian(cx, cy, radius, startAngle);
|
|
42017
|
+
var largeArc = endAngle - startAngle <= Math.PI ? '0' : '1';
|
|
42018
|
+
return 'M ' + fixed(cx) + ' ' + fixed(cy) + ' L ' + fixed(start.x) + ' ' + fixed(start.y) +
|
|
42019
|
+
' A ' + fixed(radius) + ' ' + fixed(radius) + ' 0 ' + largeArc + ' 0 ' + fixed(end.x) + ' ' + fixed(end.y) + ' Z';
|
|
42020
|
+
}
|
|
42021
|
+
|
|
42022
|
+
function polarToCartesian(cx, cy, radius, angle) { return { x: cx + radius * Math.cos(angle), y: cy + radius * Math.sin(angle) }; }
|
|
42023
|
+
function toMonth(value) {
|
|
42024
|
+
var date = new Date(String(value));
|
|
42025
|
+
if (!Number.isFinite(date.getTime())) return String(value == null ? '' : value);
|
|
42026
|
+
return date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0');
|
|
42027
|
+
}
|
|
42028
|
+
function color(index) { var palette = ['#2563eb', '#16a34a', '#f97316', '#dc2626', '#7c3aed', '#0891b2']; return palette[index % palette.length]; }
|
|
42029
|
+
function fixed(value) { return Number(value).toFixed(1); }
|
|
42030
|
+
function sum(a, b) { return a + b; }
|
|
42031
|
+
|
|
42032
|
+
function detailTable(selectedRows, chartId) {
|
|
42033
|
+
var visible = selectedRows.slice(0, 100);
|
|
42034
|
+
var columns = Object.keys(visible[0] || rows[0] || {}).slice(0, 8);
|
|
42035
|
+
if (!columns.length) return '<div class="miao-detail-title">No rows</div>';
|
|
42036
|
+
return '<div class="miao-detail-title">' + escapeHtml(chartId || 'Detail') + ': ' + selectedRows.length + ' rows</div>' +
|
|
42037
|
+
'<table><thead><tr>' + columns.map(function(col) { return '<th>' + escapeHtml(col) + '</th>'; }).join('') +
|
|
42038
|
+
'</tr></thead><tbody>' + visible.map(function(row) {
|
|
42039
|
+
return '<tr>' + columns.map(function(col) { return '<td>' + escapeHtml(row[col] == null ? '' : row[col]) + '</td>'; }).join('') + '</tr>';
|
|
42040
|
+
}).join('') + '</tbody></table>';
|
|
42041
|
+
}
|
|
42042
|
+
|
|
42043
|
+
function uniqueValues(field) {
|
|
42044
|
+
var seen = new Set();
|
|
42045
|
+
rows.forEach(function(row) {
|
|
42046
|
+
if (row[field] != null) seen.add(String(row[field]));
|
|
42047
|
+
});
|
|
42048
|
+
return Array.from(seen).slice(0, 200).sort();
|
|
42049
|
+
}
|
|
42050
|
+
|
|
42051
|
+
function comparable(value) {
|
|
42052
|
+
if (value == null || value === '') return null;
|
|
42053
|
+
var number = Number(value);
|
|
42054
|
+
if (Number.isFinite(number)) return number;
|
|
42055
|
+
var date = new Date(String(value)).getTime();
|
|
42056
|
+
return Number.isFinite(date) ? date : null;
|
|
42057
|
+
}
|
|
42058
|
+
|
|
42059
|
+
function createTooltip() {
|
|
42060
|
+
var el = document.createElement('div');
|
|
42061
|
+
el.className = 'miao-tooltip';
|
|
42062
|
+
el.hidden = true;
|
|
42063
|
+
document.body.appendChild(el);
|
|
42064
|
+
return el;
|
|
42065
|
+
}
|
|
42066
|
+
|
|
42067
|
+
function moveTooltip(event) {
|
|
42068
|
+
tooltip.style.left = event.clientX + 'px';
|
|
42069
|
+
tooltip.style.top = event.clientY + 'px';
|
|
42070
|
+
}
|
|
42071
|
+
|
|
42072
|
+
function escapeHtml(value) {
|
|
42073
|
+
return String(value).replace(/[&<>"']/g, function(char) {
|
|
42074
|
+
return ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[char];
|
|
42075
|
+
});
|
|
42076
|
+
}
|
|
42077
|
+
|
|
42078
|
+
function escapeAttr(value) {
|
|
42079
|
+
return escapeHtml(value).replace(/\\n/g, ' ');
|
|
42080
|
+
}
|
|
42081
|
+
|
|
42082
|
+
init();
|
|
42083
|
+
})();
|
|
42084
|
+
`;
|
|
42085
|
+
|
|
42086
|
+
// src/interactive-runtime.ts
|
|
42087
|
+
function shouldEnableInteractiveRuntime(spec, options = {}) {
|
|
42088
|
+
if (options.enabled === true) return true;
|
|
42089
|
+
if (options.enabled === false) return false;
|
|
42090
|
+
if ((spec.interactions?.globalFilters?.length ?? 0) > 0) return true;
|
|
42091
|
+
return spec.charts.some((chart) => Boolean(chart.interaction || chart.drilldownPreset));
|
|
42092
|
+
}
|
|
42093
|
+
function renderInteractiveAssets(rows) {
|
|
42094
|
+
return `
|
|
42095
|
+
<script type="application/json" id="miao-viz-data">${escapeScriptJson(rows)}</script>
|
|
42096
|
+
<style>${INTERACTIVE_CSS}</style>
|
|
42097
|
+
<script>${INTERACTIVE_JS}</script>`;
|
|
42098
|
+
}
|
|
42099
|
+
function escapeScriptJson(value) {
|
|
42100
|
+
return JSON.stringify(value).replace(/</g, "\\u003c");
|
|
42101
|
+
}
|
|
42102
|
+
|
|
41607
42103
|
// src/html-export.ts
|
|
41608
42104
|
var INSIGHTS_CSS = `
|
|
41609
42105
|
.report-insights { margin: 0 0 32px; padding: 16px 20px 14px; border-radius: 4px; border: 1px solid rgba(128,128,128,0.18); background: rgba(128,128,128,0.04); }
|
|
@@ -41611,9 +42107,10 @@ var INSIGHTS_CSS = `
|
|
|
41611
42107
|
.insights-list { margin: 0; padding: 0 0 0 18px; }
|
|
41612
42108
|
.insights-list li { margin: 5px 0; font-size: 13px; line-height: 1.55; opacity: 0.75; }
|
|
41613
42109
|
`;
|
|
41614
|
-
function renderStaticHtml(spec, profile, rows, themeOverride) {
|
|
42110
|
+
function renderStaticHtml(spec, profile, rows, themeOverride, interactiveOptions = {}) {
|
|
41615
42111
|
const theme = getTheme(themeOverride ?? spec.theme);
|
|
41616
42112
|
const title = spec.title ?? "Miao Vision Report";
|
|
42113
|
+
const interactive = shouldEnableInteractiveRuntime(spec, interactiveOptions);
|
|
41617
42114
|
const header = theme.layout === "editorial" ? renderEditorialHeader(title, spec.description, profile) : renderDefaultHeader(title, spec.description, profile);
|
|
41618
42115
|
const insights = spec.insights && spec.insights.length > 0 ? renderInsights(spec.insights) : "";
|
|
41619
42116
|
let charts;
|
|
@@ -41629,16 +42126,18 @@ function renderStaticHtml(spec, profile, rows, themeOverride) {
|
|
|
41629
42126
|
sections.push(renderKpiGroup(group, rows, theme));
|
|
41630
42127
|
} else {
|
|
41631
42128
|
const chart = spec.charts[i];
|
|
41632
|
-
const
|
|
41633
|
-
|
|
42129
|
+
const chartId = chartIdFor(chart, i);
|
|
42130
|
+
const svg = renderChartSvg(chart, rows, theme.svg, { chartId });
|
|
42131
|
+
sections.push(renderEditorialCard(chart, i, svg, chartId));
|
|
41634
42132
|
i++;
|
|
41635
42133
|
}
|
|
41636
42134
|
}
|
|
41637
42135
|
charts = sections.join("\n");
|
|
41638
42136
|
} else {
|
|
41639
42137
|
charts = spec.charts.map((chart, index) => {
|
|
41640
|
-
const
|
|
41641
|
-
|
|
42138
|
+
const chartId = chartIdFor(chart, index);
|
|
42139
|
+
const svg = renderChartSvg(chart, rows, theme.svg, { chartId });
|
|
42140
|
+
return renderDefaultCard(chart, index, svg, chartId);
|
|
41642
42141
|
}).join("\n");
|
|
41643
42142
|
}
|
|
41644
42143
|
return `<!doctype html>
|
|
@@ -41657,6 +42156,7 @@ function renderStaticHtml(spec, profile, rows, themeOverride) {
|
|
|
41657
42156
|
</main>
|
|
41658
42157
|
<script type="application/json" id="miao-viz-spec">${escapeHtml(JSON.stringify(spec, null, 2))}</script>
|
|
41659
42158
|
<script type="application/json" id="miao-viz-profile">${escapeHtml(JSON.stringify(profile, null, 2))}</script>
|
|
42159
|
+
${interactive ? renderInteractiveAssets(rows) : ""}
|
|
41660
42160
|
</body>
|
|
41661
42161
|
</html>`;
|
|
41662
42162
|
}
|
|
@@ -41684,11 +42184,11 @@ function renderEditorialHeader(title, description, profile) {
|
|
|
41684
42184
|
</div>
|
|
41685
42185
|
</header>`;
|
|
41686
42186
|
}
|
|
41687
|
-
function renderDefaultCard(chart, index, svg) {
|
|
42187
|
+
function renderDefaultCard(chart, index, svg, chartId) {
|
|
41688
42188
|
const chartTitle = chart.title ?? `${chart.type} chart ${index + 1}`;
|
|
41689
|
-
return `<section class="chart-block">
|
|
42189
|
+
return `<section class="chart-block" data-miao-chart="${escapeHtml(chartId)}">
|
|
41690
42190
|
<h2>${escapeHtml(chartTitle)}</h2>
|
|
41691
|
-
|
|
42191
|
+
<div class="miao-render-slot">${svg}</div>
|
|
41692
42192
|
</section>`;
|
|
41693
42193
|
}
|
|
41694
42194
|
function renderKpiGroup(charts, rows, theme) {
|
|
@@ -41698,16 +42198,19 @@ function renderKpiGroup(charts, rows, theme) {
|
|
|
41698
42198
|
<div class="kpi-grid">${items}</div>
|
|
41699
42199
|
</section>`;
|
|
41700
42200
|
}
|
|
41701
|
-
function renderEditorialCard(chart, index, svg) {
|
|
42201
|
+
function renderEditorialCard(chart, index, svg, chartId) {
|
|
41702
42202
|
const chartTitle = chart.title ?? `${chart.type} chart ${index + 1}`;
|
|
41703
42203
|
const caption = buildCaption(chart);
|
|
41704
|
-
return `<section class="chart-card">
|
|
42204
|
+
return `<section class="chart-card" data-miao-chart="${escapeHtml(chartId)}">
|
|
41705
42205
|
<div class="chart-label">${escapeHtml(chart.type.toUpperCase())} CHART</div>
|
|
41706
42206
|
<h2>${escapeHtml(chartTitle)}</h2>
|
|
41707
|
-
|
|
42207
|
+
<div class="miao-render-slot">${svg}</div>
|
|
41708
42208
|
${caption ? `<p class="chart-caption">${escapeHtml(caption)}</p>` : ""}
|
|
41709
42209
|
</section>`;
|
|
41710
42210
|
}
|
|
42211
|
+
function chartIdFor(chart, index) {
|
|
42212
|
+
return chart.id ?? `chart-${index + 1}`;
|
|
42213
|
+
}
|
|
41711
42214
|
function renderInsights(insights) {
|
|
41712
42215
|
const items = insights.map((s) => `<li>${escapeHtml(s)}</li>`).join("\n ");
|
|
41713
42216
|
return `<section class="report-insights">
|
|
@@ -56278,9 +56781,20 @@ var transformSchema = external_exports.object({
|
|
|
56278
56781
|
order: external_exports.enum(["asc", "desc"]).optional(),
|
|
56279
56782
|
value: external_exports.unknown().optional()
|
|
56280
56783
|
});
|
|
56784
|
+
var globalFilterSchema = external_exports.object({
|
|
56785
|
+
field: external_exports.string().min(1),
|
|
56786
|
+
type: external_exports.enum(["select", "range"])
|
|
56787
|
+
});
|
|
56788
|
+
var chartInteractionSchema = external_exports.object({
|
|
56789
|
+
tooltip: external_exports.boolean().optional(),
|
|
56790
|
+
select: external_exports.enum(["filter", "detail"]).optional()
|
|
56791
|
+
});
|
|
56281
56792
|
var chartSpecSchema = external_exports.object({
|
|
56793
|
+
id: external_exports.string().min(1).optional(),
|
|
56282
56794
|
type: external_exports.enum(MVP_CHART_TYPES),
|
|
56283
56795
|
title: external_exports.string().optional(),
|
|
56796
|
+
interaction: chartInteractionSchema.optional(),
|
|
56797
|
+
drilldownPreset: external_exports.enum(["category-detail"]).optional(),
|
|
56284
56798
|
data: external_exports.object({
|
|
56285
56799
|
source: external_exports.string().optional(),
|
|
56286
56800
|
transform: external_exports.array(transformSchema).optional()
|
|
@@ -56299,6 +56813,9 @@ var reportSpecSchema = external_exports.object({
|
|
|
56299
56813
|
title: external_exports.string().optional(),
|
|
56300
56814
|
description: external_exports.string().optional(),
|
|
56301
56815
|
theme: external_exports.enum(["default", "editorial", "dark", "minimal"]).optional(),
|
|
56816
|
+
interactions: external_exports.object({
|
|
56817
|
+
globalFilters: external_exports.array(globalFilterSchema).optional()
|
|
56818
|
+
}).optional(),
|
|
56302
56819
|
insights: external_exports.array(external_exports.string()).optional(),
|
|
56303
56820
|
charts: external_exports.array(chartSpecSchema).min(1)
|
|
56304
56821
|
});
|
|
@@ -56332,6 +56849,7 @@ var REQUIRED_ENCODINGS = {
|
|
|
56332
56849
|
table: [],
|
|
56333
56850
|
bigvalue: ["value"]
|
|
56334
56851
|
};
|
|
56852
|
+
var DRILLDOWN_CHART_TYPES = ["bar", "pie", "table"];
|
|
56335
56853
|
function validateReportSpec(spec, profile, formats = ["html"]) {
|
|
56336
56854
|
const parsed = reportSpecSchema.safeParse(spec);
|
|
56337
56855
|
if (!parsed.success) {
|
|
@@ -56345,11 +56863,24 @@ function validateReportSpec(spec, profile, formats = ["html"]) {
|
|
|
56345
56863
|
}
|
|
56346
56864
|
}
|
|
56347
56865
|
const availableFields = profile.columns.map((column) => column.name);
|
|
56866
|
+
const chartIds = /* @__PURE__ */ new Set();
|
|
56867
|
+
const interactionResult = validateReportInteractions(parsed.data, profile, availableFields);
|
|
56868
|
+
if (isAgentError(interactionResult)) return interactionResult;
|
|
56348
56869
|
for (const chart of parsed.data.charts) {
|
|
56870
|
+
if (chart.id) {
|
|
56871
|
+
if (chartIds.has(chart.id)) {
|
|
56872
|
+
return agentError("DUPLICATE_CHART_ID", `Chart id '${chart.id}' is used more than once.`, {
|
|
56873
|
+
chartId: chart.id
|
|
56874
|
+
});
|
|
56875
|
+
}
|
|
56876
|
+
chartIds.add(chart.id);
|
|
56877
|
+
}
|
|
56349
56878
|
const chartTypeResult = validateChartType(chart);
|
|
56350
56879
|
if (isAgentError(chartTypeResult)) return chartTypeResult;
|
|
56351
56880
|
const encodingResult = validateRequiredEncodings(chart);
|
|
56352
56881
|
if (isAgentError(encodingResult)) return encodingResult;
|
|
56882
|
+
const chartInteractionResult = validateChartInteraction(chart);
|
|
56883
|
+
if (isAgentError(chartInteractionResult)) return chartInteractionResult;
|
|
56353
56884
|
const derivedFields = collectDerivedFields(chart);
|
|
56354
56885
|
const sourceFields = collectSourceFields(chart, derivedFields);
|
|
56355
56886
|
for (const field of sourceFields) {
|
|
@@ -56392,6 +56923,46 @@ function validateRequiredEncodings(chart) {
|
|
|
56392
56923
|
}
|
|
56393
56924
|
return ok(chart);
|
|
56394
56925
|
}
|
|
56926
|
+
function validateReportInteractions(spec, profile, availableFields) {
|
|
56927
|
+
for (const filter of spec.interactions?.globalFilters ?? []) {
|
|
56928
|
+
const column = profile.columns.find((candidate) => candidate.name === filter.field);
|
|
56929
|
+
if (!column) {
|
|
56930
|
+
return agentError("INTERACTION_FIELD_NOT_FOUND", `Interactive filter field '${filter.field}' was not found in the input data.`, {
|
|
56931
|
+
field: filter.field,
|
|
56932
|
+
availableFields
|
|
56933
|
+
});
|
|
56934
|
+
}
|
|
56935
|
+
if (filter.type === "range" && column.type !== "number" && column.type !== "date") {
|
|
56936
|
+
return agentError("INTERACTION_FILTER_TYPE_MISMATCH", `Range filter '${filter.field}' requires a number or date field.`, {
|
|
56937
|
+
field: filter.field,
|
|
56938
|
+
filterType: filter.type,
|
|
56939
|
+
columnType: column.type,
|
|
56940
|
+
supportedColumnTypes: ["number", "date"]
|
|
56941
|
+
});
|
|
56942
|
+
}
|
|
56943
|
+
}
|
|
56944
|
+
return ok(spec);
|
|
56945
|
+
}
|
|
56946
|
+
function validateChartInteraction(chart) {
|
|
56947
|
+
if (chart.drilldownPreset && chart.drilldownPreset !== "category-detail") {
|
|
56948
|
+
return agentError("UNSUPPORTED_DRILLDOWN_PRESET", `Drilldown preset '${chart.drilldownPreset}' is not supported.`, {
|
|
56949
|
+
supportedPresets: ["category-detail"]
|
|
56950
|
+
});
|
|
56951
|
+
}
|
|
56952
|
+
if (chart.drilldownPreset && !DRILLDOWN_CHART_TYPES.includes(chart.type)) {
|
|
56953
|
+
return agentError("UNSUPPORTED_DRILLDOWN_CHART_TYPE", `Drilldown preset '${chart.drilldownPreset}' is not supported for chart type '${chart.type}'.`, {
|
|
56954
|
+
chartType: chart.type,
|
|
56955
|
+
supportedChartTypes: DRILLDOWN_CHART_TYPES
|
|
56956
|
+
});
|
|
56957
|
+
}
|
|
56958
|
+
if (chart.interaction?.select && !DRILLDOWN_CHART_TYPES.includes(chart.type)) {
|
|
56959
|
+
return agentError("UNSUPPORTED_INTERACTION_CHART_TYPE", `Chart interaction select '${chart.interaction.select}' is not supported for chart type '${chart.type}'.`, {
|
|
56960
|
+
chartType: chart.type,
|
|
56961
|
+
supportedChartTypes: DRILLDOWN_CHART_TYPES
|
|
56962
|
+
});
|
|
56963
|
+
}
|
|
56964
|
+
return ok(chart);
|
|
56965
|
+
}
|
|
56395
56966
|
function collectSourceFields(chart, derivedFields) {
|
|
56396
56967
|
const fields = /* @__PURE__ */ new Set();
|
|
56397
56968
|
for (const encoding of Object.values(chart.encoding)) {
|
|
@@ -56601,8 +57172,8 @@ var SLIDE_CSS = `
|
|
|
56601
57172
|
|
|
56602
57173
|
/* \u2500\u2500 Shared slide elements \u2500\u2500 */
|
|
56603
57174
|
.slide-eyebrow { font-family: var(--mv-mono); font-size: 11px; font-weight: 500; text-transform: uppercase; letter-spacing: 3px; color: var(--mv-muted); margin-bottom: 14px; }
|
|
56604
|
-
.slide-title { font-family: var(--mv-serif); font-size: 50px; font-weight: 500; line-height: 1.1; letter-spacing: -0.6px; color: var(--mv-ink); margin-bottom: 24px; }
|
|
56605
|
-
.slide-claim { font-family: var(--mv-serif); font-size: 21px; line-height: 1.45; color: var(--mv-soft); margin-bottom: 18px; max-width: 52ch; }
|
|
57175
|
+
.slide-title { font-family: var(--mv-serif); font-size: 50px; font-weight: 500; line-height: 1.1; letter-spacing: -0.6px; color: var(--mv-ink); margin-bottom: 24px; max-width: 19ch; overflow-wrap: anywhere; }
|
|
57176
|
+
.slide-claim { font-family: var(--mv-serif); font-size: 21px; line-height: 1.45; color: var(--mv-soft); margin-bottom: 18px; max-width: 52ch; overflow-wrap: anywhere; }
|
|
56606
57177
|
.slide-pts { list-style: none; counter-reset: pts; }
|
|
56607
57178
|
.slide-pts li { counter-increment: pts; font-size: 16px; line-height: 1.55; color: var(--mv-soft); padding-left: 22px; position: relative; margin-bottom: 10px; }
|
|
56608
57179
|
.slide-pts li::before { content: counter(pts) "."; position: absolute; left: 0; color: var(--mv-brand); font-weight: 500; font-family: var(--mv-mono); font-size: 13px; }
|
|
@@ -56613,7 +57184,7 @@ var SLIDE_CSS = `
|
|
|
56613
57184
|
.slide-chart-full svg, .slide-chart-full .miao-table-wrap { width: 100%; }
|
|
56614
57185
|
|
|
56615
57186
|
/* \u2500\u2500 Metrics \u2500\u2500 */
|
|
56616
|
-
.slide-metrics { display:
|
|
57187
|
+
.slide-metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; padding-top: 14px; border-top: 0.5px dotted var(--mv-border); margin-bottom: 20px; }
|
|
56617
57188
|
.slide-metric { flex: 1; display: flex; flex-direction: column; gap: 5px; padding-right: 24px; }
|
|
56618
57189
|
.slide-metric .v { font-family: var(--mv-serif); font-size: 42px; font-weight: 500; color: var(--mv-brand); line-height: 1; font-variant-numeric: tabular-nums; letter-spacing: -0.5px; }
|
|
56619
57190
|
.slide-metric .l { font-family: var(--mv-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 1.5px; color: var(--mv-muted); }
|
|
@@ -56621,8 +57192,8 @@ var SLIDE_CSS = `
|
|
|
56621
57192
|
/* \u2500\u2500 Cover \u2500\u2500 */
|
|
56622
57193
|
.slide-cover { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: center; height: 100%; }
|
|
56623
57194
|
.slide-cover-left { display: flex; flex-direction: column; }
|
|
56624
|
-
.slide-cover h1 { font-family: var(--mv-serif); font-size: 56px; font-weight: 500; line-height: 1.05; letter-spacing: -1px; color: var(--mv-ink); margin-bottom: 14px; }
|
|
56625
|
-
.slide-cover .sub { font-size: 18px; color: var(--mv-soft); line-height: 1.5; margin-bottom: 22px; max-width: 34ch; }
|
|
57195
|
+
.slide-cover h1 { font-family: var(--mv-serif); font-size: 56px; font-weight: 500; line-height: 1.05; letter-spacing: -1px; color: var(--mv-ink); margin-bottom: 14px; max-width: 12ch; overflow-wrap: anywhere; }
|
|
57196
|
+
.slide-cover .sub { font-size: 18px; color: var(--mv-soft); line-height: 1.5; margin-bottom: 22px; max-width: 34ch; overflow-wrap: anywhere; }
|
|
56626
57197
|
.slide-cover .line { width: 48px; height: 2px; background: var(--mv-brand); margin-bottom: 14px; }
|
|
56627
57198
|
.slide-cover .meta { font-family: var(--mv-mono); font-size: 11px; color: var(--mv-muted); letter-spacing: 0.5px; }
|
|
56628
57199
|
.slide-cover-right { display: flex; align-items: center; justify-content: center; }
|
|
@@ -56643,6 +57214,8 @@ var SLIDE_CSS = `
|
|
|
56643
57214
|
|
|
56644
57215
|
/* \u2500\u2500 Table styles inside slide \u2500\u2500 */
|
|
56645
57216
|
.miao-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
57217
|
+
.miao-table-wrap { max-height: 430px; overflow: hidden; }
|
|
57218
|
+
.miao-table caption { caption-side: top; text-align: left; padding: 0 0 10px; color: var(--mv-muted); font-family: var(--mv-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; }
|
|
56646
57219
|
.miao-table th { border-bottom: 1px solid var(--mv-border); padding: 7px 12px; text-align: left; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em; color: var(--mv-muted); font-family: var(--mv-mono); }
|
|
56647
57220
|
.miao-table td { border-bottom: 1px solid var(--mv-border); padding: 8px 12px; color: var(--mv-soft); font-variant-numeric: tabular-nums; }
|
|
56648
57221
|
.miao-table tbody tr:last-child td { border-bottom: none; }
|
|
@@ -56682,8 +57255,8 @@ var SLIDE_JS = `
|
|
|
56682
57255
|
if (e.key === 'ArrowRight' || e.key === ' ') { e.preventDefault(); goTo(current + 1); }
|
|
56683
57256
|
else if (e.key === 'ArrowLeft') { e.preventDefault(); goTo(current - 1); }
|
|
56684
57257
|
else if (e.key === 'f' || e.key === 'F') {
|
|
56685
|
-
if (!document.fullscreenElement) document.documentElement.requestFullscreen();
|
|
56686
|
-
else document.exitFullscreen();
|
|
57258
|
+
if (!document.fullscreenElement) document.documentElement.requestFullscreen().catch(function() {});
|
|
57259
|
+
else document.exitFullscreen().catch(function() {});
|
|
56687
57260
|
}
|
|
56688
57261
|
});
|
|
56689
57262
|
|
|
@@ -56694,8 +57267,8 @@ var SLIDE_JS = `
|
|
|
56694
57267
|
if (btnPrev) btnPrev.addEventListener('click', function() { goTo(current - 1); });
|
|
56695
57268
|
if (btnNext) btnNext.addEventListener('click', function() { goTo(current + 1); });
|
|
56696
57269
|
if (btnFs) btnFs.addEventListener('click', function() {
|
|
56697
|
-
if (!document.fullscreenElement) document.documentElement.requestFullscreen();
|
|
56698
|
-
else document.exitFullscreen();
|
|
57270
|
+
if (!document.fullscreenElement) document.documentElement.requestFullscreen().catch(function() {});
|
|
57271
|
+
else document.exitFullscreen().catch(function() {});
|
|
56699
57272
|
});
|
|
56700
57273
|
if (btnPrint) btnPrint.addEventListener('click', function() {
|
|
56701
57274
|
document.body.classList.remove('present-mode');
|
|
@@ -56977,8 +57550,310 @@ function hintForIssue(path, message) {
|
|
|
56977
57550
|
return `Check ${path} in the DeckSpec.`;
|
|
56978
57551
|
}
|
|
56979
57552
|
|
|
57553
|
+
// src/article-infographic.ts
|
|
57554
|
+
var import_node_fs2 = require("node:fs");
|
|
57555
|
+
var import_node_path2 = require("node:path");
|
|
57556
|
+
var ARTICLE_STYLES = ["editorial", "executive", "minimal"];
|
|
57557
|
+
var ARTICLE_FORMATS = ["html", "json", "markdown"];
|
|
57558
|
+
var DATE_PATTERN = /\b(?:\d{4}(?:[-/]\d{1,2}(?:[-/]\d{1,2})?)?|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\s+\d{1,2},?\s+\d{4})\b/i;
|
|
57559
|
+
var NUMBER_PATTERN = /(?:[$¥€]\s?\d[\d,.]*|\b\d+(?:\.\d+)?%?\b)/;
|
|
57560
|
+
function parseArticleStyle(value) {
|
|
57561
|
+
if (!value) return "editorial";
|
|
57562
|
+
return ARTICLE_STYLES.includes(value) ? value : void 0;
|
|
57563
|
+
}
|
|
57564
|
+
function parseArticleFormat(value) {
|
|
57565
|
+
if (!value) return "html";
|
|
57566
|
+
return ARTICLE_FORMATS.includes(value) ? value : void 0;
|
|
57567
|
+
}
|
|
57568
|
+
function generateInfographicFromFile(file2, style) {
|
|
57569
|
+
const extension = (0, import_node_path2.extname)(file2).toLowerCase();
|
|
57570
|
+
if (extension && ![".md", ".markdown", ".txt"].includes(extension)) {
|
|
57571
|
+
return agentError("UNSUPPORTED_ARTICLE_INPUT", "Article input must be a Markdown or plain-text file.", {
|
|
57572
|
+
supportedExtensions: [".md", ".markdown", ".txt"]
|
|
57573
|
+
});
|
|
57574
|
+
}
|
|
57575
|
+
let raw;
|
|
57576
|
+
try {
|
|
57577
|
+
raw = (0, import_node_fs2.readFileSync)(file2, "utf8");
|
|
57578
|
+
} catch (error51) {
|
|
57579
|
+
return agentError("ARTICLE_INPUT_UNREADABLE", error51 instanceof Error ? error51.message : "Article input could not be read.", {
|
|
57580
|
+
file: file2
|
|
57581
|
+
});
|
|
57582
|
+
}
|
|
57583
|
+
const normalized = normalizeArticleText(raw);
|
|
57584
|
+
if (!normalized) {
|
|
57585
|
+
return agentError("EMPTY_ARTICLE_INPUT", "Article input is empty after normalization.", { file: file2 });
|
|
57586
|
+
}
|
|
57587
|
+
const parsed = parseArticle(normalized, file2);
|
|
57588
|
+
const spec = buildInfographicSpec(parsed, style, file2);
|
|
57589
|
+
return ok({ spec, markdown: renderInfographicMarkdown(spec) });
|
|
57590
|
+
}
|
|
57591
|
+
function normalizeArticleText(raw) {
|
|
57592
|
+
return raw.replace(/\r\n/g, "\n").replace(/\t/g, " ").split("\n").map((line) => line.replace(/[ \u00a0]+$/g, "")).join("\n").trim();
|
|
57593
|
+
}
|
|
57594
|
+
function parseArticle(text, file2) {
|
|
57595
|
+
const lines = text.split("\n");
|
|
57596
|
+
const metadata = extractMetadata(lines);
|
|
57597
|
+
const title = findTitle(lines) ?? titleFromFilename(file2);
|
|
57598
|
+
const contentLines = lines.filter((line) => {
|
|
57599
|
+
const trimmed = line.trim();
|
|
57600
|
+
return trimmed !== title && !trimmed.match(/^#\s+/) && !trimmed.match(/^(source|url|author|date):\s*/i);
|
|
57601
|
+
});
|
|
57602
|
+
const quotes = contentLines.filter((line) => line.trim().startsWith(">")).map((line) => cleanMarkdown(line.replace(/^>\s?/, ""))).filter(Boolean);
|
|
57603
|
+
const listItems = contentLines.filter((line) => line.trim().match(/^[-*+]\s+/) || line.trim().match(/^\d+\.\s+/)).map((line) => cleanMarkdown(line.replace(/^\s*(?:[-*+]|\d+\.)\s+/, ""))).filter(Boolean);
|
|
57604
|
+
const tableRows = extractTableRows(contentLines);
|
|
57605
|
+
const paragraphs = contentLines.join("\n").split(/\n{2,}/).map((block) => cleanMarkdown(block.replace(/\n/g, " "))).filter((block) => block.length > 0 && !block.startsWith("|") && !block.match(/^[-*+]\s+/));
|
|
57606
|
+
return {
|
|
57607
|
+
title,
|
|
57608
|
+
subtitle: metadata.subtitle ?? firstUsefulParagraph(paragraphs),
|
|
57609
|
+
source: metadata.source,
|
|
57610
|
+
paragraphs,
|
|
57611
|
+
listItems,
|
|
57612
|
+
quotes,
|
|
57613
|
+
tableRows
|
|
57614
|
+
};
|
|
57615
|
+
}
|
|
57616
|
+
function buildInfographicSpec(parsed, style, file2) {
|
|
57617
|
+
const evidence = [...parsed.listItems, ...sentences(parsed.paragraphs.join(" "))];
|
|
57618
|
+
const facts = collectFacts(evidence);
|
|
57619
|
+
const timeline = collectTimeline(evidence);
|
|
57620
|
+
const comparison = collectComparison(evidence, parsed.tableRows);
|
|
57621
|
+
const takeaways = collectTakeaways(evidence, facts);
|
|
57622
|
+
const summary = parsed.subtitle ?? takeaways[0]?.text ?? facts[0]?.text ?? "A concise visual summary of the source article.";
|
|
57623
|
+
const sections = [
|
|
57624
|
+
{
|
|
57625
|
+
type: "hero",
|
|
57626
|
+
title: parsed.title,
|
|
57627
|
+
emphasis: summary,
|
|
57628
|
+
items: [{ text: summary }]
|
|
57629
|
+
}
|
|
57630
|
+
];
|
|
57631
|
+
if (facts.length > 0) sections.push({ type: "facts", title: "Key Facts", items: facts.slice(0, 6) });
|
|
57632
|
+
if (timeline.length > 1) sections.push({ type: "timeline", title: "Timeline", items: timeline.slice(0, 6) });
|
|
57633
|
+
if (comparison.length > 1) sections.push({ type: "comparison", title: "Comparison", items: comparison.slice(0, 6) });
|
|
57634
|
+
if (parsed.quotes.length > 0) {
|
|
57635
|
+
sections.push({
|
|
57636
|
+
type: "quote",
|
|
57637
|
+
title: "Notable Quote",
|
|
57638
|
+
emphasis: parsed.quotes[0],
|
|
57639
|
+
items: parsed.quotes.slice(0, 3).map((text) => ({ text }))
|
|
57640
|
+
});
|
|
57641
|
+
}
|
|
57642
|
+
if (takeaways.length > 0) sections.push({ type: "takeaways", title: "Takeaways", items: takeaways.slice(0, 5) });
|
|
57643
|
+
return {
|
|
57644
|
+
title: parsed.title,
|
|
57645
|
+
subtitle: parsed.subtitle,
|
|
57646
|
+
source: parsed.source,
|
|
57647
|
+
style,
|
|
57648
|
+
summary,
|
|
57649
|
+
sections,
|
|
57650
|
+
metadata: {
|
|
57651
|
+
inputFile: file2,
|
|
57652
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
57653
|
+
wordCount: parsed.paragraphs.join(" ").split(/\s+/).filter(Boolean).length
|
|
57654
|
+
}
|
|
57655
|
+
};
|
|
57656
|
+
}
|
|
57657
|
+
function renderInfographicMarkdown(spec) {
|
|
57658
|
+
const lines = [`# ${spec.title}`, ""];
|
|
57659
|
+
if (spec.subtitle) lines.push(spec.subtitle, "");
|
|
57660
|
+
if (spec.source) lines.push(`Source: ${spec.source}`, "");
|
|
57661
|
+
lines.push(`Style: ${spec.style}`, "", `## Summary`, "", spec.summary, "");
|
|
57662
|
+
for (const section of spec.sections.filter((section2) => section2.type !== "hero")) {
|
|
57663
|
+
lines.push(`## ${section.title}`, "");
|
|
57664
|
+
for (const item of section.items) {
|
|
57665
|
+
const prefix = item.label ? `**${item.label}:** ` : "";
|
|
57666
|
+
lines.push(`- ${prefix}${item.value ? `${item.value} \u2014 ` : ""}${item.text}`);
|
|
57667
|
+
}
|
|
57668
|
+
lines.push("");
|
|
57669
|
+
}
|
|
57670
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
57671
|
+
}
|
|
57672
|
+
function extractMetadata(lines) {
|
|
57673
|
+
const sourceLine = lines.find((line) => line.match(/^(source|url):\s*/i));
|
|
57674
|
+
const subtitleLine = lines.find((line) => line.match(/^subtitle:\s*/i));
|
|
57675
|
+
return {
|
|
57676
|
+
source: sourceLine?.replace(/^(source|url):\s*/i, "").trim(),
|
|
57677
|
+
subtitle: subtitleLine?.replace(/^subtitle:\s*/i, "").trim()
|
|
57678
|
+
};
|
|
57679
|
+
}
|
|
57680
|
+
function findTitle(lines) {
|
|
57681
|
+
const heading = lines.find((line) => line.trim().match(/^#\s+\S/));
|
|
57682
|
+
if (heading) return cleanMarkdown(heading.replace(/^#\s+/, ""));
|
|
57683
|
+
const first = lines.find((line) => line.trim() && !line.match(/^(source|url|author|date):\s*/i));
|
|
57684
|
+
return first ? cleanMarkdown(first).slice(0, 120) : void 0;
|
|
57685
|
+
}
|
|
57686
|
+
function titleFromFilename(file2) {
|
|
57687
|
+
return (0, import_node_path2.basename)(file2, (0, import_node_path2.extname)(file2)).replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
57688
|
+
}
|
|
57689
|
+
function extractTableRows(lines) {
|
|
57690
|
+
return lines.filter((line) => line.includes("|") && !line.match(/^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)+\|?\s*$/)).map((line) => line.split("|").map((cell) => cleanMarkdown(cell)).filter(Boolean)).filter((row) => row.length > 1);
|
|
57691
|
+
}
|
|
57692
|
+
function collectFacts(candidates) {
|
|
57693
|
+
return uniqueItems(candidates.filter((text) => NUMBER_PATTERN.test(text)).map((text) => ({
|
|
57694
|
+
value: text.match(NUMBER_PATTERN)?.[0],
|
|
57695
|
+
text: compactText(text, 150)
|
|
57696
|
+
})));
|
|
57697
|
+
}
|
|
57698
|
+
function collectTimeline(candidates) {
|
|
57699
|
+
return uniqueItems(candidates.filter((text) => DATE_PATTERN.test(text)).map((text) => ({
|
|
57700
|
+
label: text.match(DATE_PATTERN)?.[0],
|
|
57701
|
+
text: compactText(text, 150)
|
|
57702
|
+
})));
|
|
57703
|
+
}
|
|
57704
|
+
function collectComparison(candidates, tableRows) {
|
|
57705
|
+
const tableItems = tableRows.slice(1).map((row) => ({
|
|
57706
|
+
label: row[0],
|
|
57707
|
+
text: row.slice(1).join(" \u2014 ")
|
|
57708
|
+
}));
|
|
57709
|
+
const textItems = candidates.filter((text) => /\b(vs\.?|versus|compared with|compared to|whereas|while)\b/i.test(text)).map((text) => ({ text: compactText(text, 160) }));
|
|
57710
|
+
return uniqueItems([...tableItems, ...textItems]);
|
|
57711
|
+
}
|
|
57712
|
+
function collectTakeaways(candidates, facts) {
|
|
57713
|
+
const explicit = candidates.filter((text) => /\b(key|takeaway|therefore|recommend|should|must|need to|in summary|conclusion|next)\b/i.test(text)).map((text) => ({ text: compactText(text, 160) }));
|
|
57714
|
+
if (explicit.length > 0) return uniqueItems(explicit);
|
|
57715
|
+
return facts.slice(0, 3).map((item) => ({ text: item.text }));
|
|
57716
|
+
}
|
|
57717
|
+
function sentences(text) {
|
|
57718
|
+
return text.split(/(?<=[.!?。!?])\s+/).map(cleanMarkdown).filter((sentence) => sentence.length > 20);
|
|
57719
|
+
}
|
|
57720
|
+
function firstUsefulParagraph(paragraphs) {
|
|
57721
|
+
return paragraphs.find((paragraph) => paragraph.length > 40)?.slice(0, 220);
|
|
57722
|
+
}
|
|
57723
|
+
function uniqueItems(items) {
|
|
57724
|
+
const seen = /* @__PURE__ */ new Set();
|
|
57725
|
+
return items.filter((item) => {
|
|
57726
|
+
const key = item.text.toLowerCase();
|
|
57727
|
+
if (seen.has(key)) return false;
|
|
57728
|
+
seen.add(key);
|
|
57729
|
+
return true;
|
|
57730
|
+
});
|
|
57731
|
+
}
|
|
57732
|
+
function compactText(text, max) {
|
|
57733
|
+
const clean = cleanMarkdown(text);
|
|
57734
|
+
return clean.length > max ? `${clean.slice(0, max - 1).trim()}...` : clean;
|
|
57735
|
+
}
|
|
57736
|
+
function cleanMarkdown(value) {
|
|
57737
|
+
return value.replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^#+\s*/, "").replace(/\s+/g, " ").trim();
|
|
57738
|
+
}
|
|
57739
|
+
|
|
57740
|
+
// src/article-html.ts
|
|
57741
|
+
function renderInfographicHtml(spec) {
|
|
57742
|
+
return `<!doctype html>
|
|
57743
|
+
<html lang="en">
|
|
57744
|
+
<head>
|
|
57745
|
+
<meta charset="utf-8" />
|
|
57746
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
57747
|
+
<title>${escapeHtml(spec.title)}</title>
|
|
57748
|
+
<meta name="generator" content="Miao Vision article infographic" />
|
|
57749
|
+
${spec.source ? `<meta name="source" content="${escapeHtml(spec.source)}" />` : ""}
|
|
57750
|
+
<style>${buildCss(spec.style)}</style>
|
|
57751
|
+
</head>
|
|
57752
|
+
<body>
|
|
57753
|
+
<main class="mv-infographic mv-infographic-${spec.style}">
|
|
57754
|
+
${spec.sections.map(renderSection).join("\n")}
|
|
57755
|
+
</main>
|
|
57756
|
+
<script type="application/json" id="miao-infographic-spec">${escapeHtml(JSON.stringify(spec, null, 2))}</script>
|
|
57757
|
+
</body>
|
|
57758
|
+
</html>`;
|
|
57759
|
+
}
|
|
57760
|
+
function renderSection(section) {
|
|
57761
|
+
if (section.type === "hero") return renderHero(section);
|
|
57762
|
+
if (section.type === "facts") return renderFacts(section);
|
|
57763
|
+
if (section.type === "timeline") return renderTimeline(section);
|
|
57764
|
+
if (section.type === "comparison") return renderComparison(section);
|
|
57765
|
+
if (section.type === "quote") return renderQuote(section);
|
|
57766
|
+
return renderTakeaways(section);
|
|
57767
|
+
}
|
|
57768
|
+
function renderHero(section) {
|
|
57769
|
+
const lead = section.emphasis ?? section.items[0]?.text ?? "";
|
|
57770
|
+
return `<section class="mv-hero">
|
|
57771
|
+
<p class="mv-eyebrow">Miao Vision Infographic</p>
|
|
57772
|
+
<h1>${escapeHtml(section.title)}</h1>
|
|
57773
|
+
<p class="mv-lead">${escapeHtml(lead)}</p>
|
|
57774
|
+
</section>`;
|
|
57775
|
+
}
|
|
57776
|
+
function renderFacts(section) {
|
|
57777
|
+
return `<section class="mv-section mv-facts">
|
|
57778
|
+
<div class="mv-section-head"><span>01</span><h2>${escapeHtml(section.title)}</h2></div>
|
|
57779
|
+
<div class="mv-fact-grid">
|
|
57780
|
+
${section.items.map((item) => `<article class="mv-fact">
|
|
57781
|
+
${item.value ? `<strong>${escapeHtml(item.value)}</strong>` : ""}
|
|
57782
|
+
<p>${escapeHtml(item.text)}</p>
|
|
57783
|
+
</article>`).join("\n")}
|
|
57784
|
+
</div>
|
|
57785
|
+
</section>`;
|
|
57786
|
+
}
|
|
57787
|
+
function renderTimeline(section) {
|
|
57788
|
+
return `<section class="mv-section mv-timeline">
|
|
57789
|
+
<div class="mv-section-head"><span>02</span><h2>${escapeHtml(section.title)}</h2></div>
|
|
57790
|
+
<ol>
|
|
57791
|
+
${section.items.map((item) => `<li><time>${escapeHtml(item.label ?? "")}</time><p>${escapeHtml(item.text)}</p></li>`).join("\n")}
|
|
57792
|
+
</ol>
|
|
57793
|
+
</section>`;
|
|
57794
|
+
}
|
|
57795
|
+
function renderComparison(section) {
|
|
57796
|
+
return `<section class="mv-section mv-comparison">
|
|
57797
|
+
<div class="mv-section-head"><span>03</span><h2>${escapeHtml(section.title)}</h2></div>
|
|
57798
|
+
<div class="mv-comparison-grid">
|
|
57799
|
+
${section.items.map((item) => `<article>
|
|
57800
|
+
${item.label ? `<h3>${escapeHtml(item.label)}</h3>` : ""}
|
|
57801
|
+
<p>${escapeHtml(item.text)}</p>
|
|
57802
|
+
</article>`).join("\n")}
|
|
57803
|
+
</div>
|
|
57804
|
+
</section>`;
|
|
57805
|
+
}
|
|
57806
|
+
function renderQuote(section) {
|
|
57807
|
+
const quote = section.emphasis ?? section.items[0]?.text ?? "";
|
|
57808
|
+
return `<section class="mv-section mv-quote">
|
|
57809
|
+
<blockquote>${escapeHtml(quote)}</blockquote>
|
|
57810
|
+
</section>`;
|
|
57811
|
+
}
|
|
57812
|
+
function renderTakeaways(section) {
|
|
57813
|
+
return `<section class="mv-section mv-takeaways">
|
|
57814
|
+
<div class="mv-section-head"><span>04</span><h2>${escapeHtml(section.title)}</h2></div>
|
|
57815
|
+
<ul>
|
|
57816
|
+
${section.items.map((item) => `<li>${escapeHtml(item.text)}</li>`).join("\n")}
|
|
57817
|
+
</ul>
|
|
57818
|
+
</section>`;
|
|
57819
|
+
}
|
|
57820
|
+
function buildCss(style) {
|
|
57821
|
+
const palette = style === "minimal" ? { bg: "#ffffff", ink: "#161616", muted: "#666666", card: "#ffffff", accent: "#111111", line: "#d8d8d8" } : style === "executive" ? { bg: "#f4f0e8", ink: "#18212f", muted: "#667085", card: "#ffffff", accent: "#1f5d8c", line: "#d7c9b8" } : { bg: "#f7efe2", ink: "#241b16", muted: "#75695d", card: "#fffaf2", accent: "#b64f2a", line: "#dfcdb7" };
|
|
57822
|
+
return `
|
|
57823
|
+
:root { color-scheme: light; --bg:${palette.bg}; --ink:${palette.ink}; --muted:${palette.muted}; --card:${palette.card}; --accent:${palette.accent}; --line:${palette.line}; }
|
|
57824
|
+
* { box-sizing: border-box; }
|
|
57825
|
+
body { margin: 0; background: var(--bg); color: var(--ink); font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
|
57826
|
+
.mv-infographic { width: min(1120px, calc(100% - 32px)); margin: 0 auto; padding: 48px 0 64px; }
|
|
57827
|
+
.mv-hero { min-height: 42vh; display: grid; align-content: center; border-bottom: 1px solid var(--line); padding: 28px 0 44px; }
|
|
57828
|
+
.mv-eyebrow { margin: 0 0 18px; color: var(--accent); font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.14em; }
|
|
57829
|
+
h1 { max-width: 860px; margin: 0; font-size: 58px; line-height: 1.02; letter-spacing: 0; }
|
|
57830
|
+
.mv-lead { max-width: 760px; margin: 22px 0 0; color: var(--muted); font-size: 21px; line-height: 1.55; }
|
|
57831
|
+
.mv-section { padding: 34px 0; border-bottom: 1px solid var(--line); }
|
|
57832
|
+
.mv-section-head { display: flex; align-items: baseline; gap: 14px; margin-bottom: 20px; }
|
|
57833
|
+
.mv-section-head span { color: var(--accent); font-weight: 800; font-size: 12px; }
|
|
57834
|
+
h2 { margin: 0; font-size: 28px; line-height: 1.15; letter-spacing: 0; }
|
|
57835
|
+
.mv-fact-grid, .mv-comparison-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
|
|
57836
|
+
.mv-fact, .mv-comparison article { background: var(--card); border: 1px solid var(--line); border-radius: 6px; padding: 18px; }
|
|
57837
|
+
.mv-fact strong { display: block; color: var(--accent); font-size: 30px; line-height: 1; margin-bottom: 12px; }
|
|
57838
|
+
.mv-fact p, .mv-comparison p, .mv-timeline p { margin: 0; color: var(--muted); line-height: 1.5; }
|
|
57839
|
+
.mv-comparison h3 { margin: 0 0 8px; font-size: 16px; }
|
|
57840
|
+
.mv-timeline ol { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
|
|
57841
|
+
.mv-timeline li { display: grid; grid-template-columns: 150px 1fr; gap: 18px; align-items: start; background: var(--card); border: 1px solid var(--line); border-radius: 6px; padding: 16px; }
|
|
57842
|
+
.mv-timeline time { color: var(--accent); font-weight: 800; }
|
|
57843
|
+
.mv-quote blockquote { margin: 0; max-width: 900px; color: var(--ink); font-size: 34px; line-height: 1.25; font-weight: 750; }
|
|
57844
|
+
.mv-takeaways ul { margin: 0; padding-left: 22px; display: grid; gap: 10px; color: var(--muted); line-height: 1.55; }
|
|
57845
|
+
@media (max-width: 720px) {
|
|
57846
|
+
.mv-infographic { width: min(100% - 24px, 1120px); padding-top: 28px; }
|
|
57847
|
+
h1 { font-size: 38px; }
|
|
57848
|
+
.mv-lead { font-size: 18px; }
|
|
57849
|
+
.mv-timeline li { grid-template-columns: 1fr; gap: 6px; }
|
|
57850
|
+
.mv-quote blockquote { font-size: 25px; }
|
|
57851
|
+
}
|
|
57852
|
+
`;
|
|
57853
|
+
}
|
|
57854
|
+
|
|
56980
57855
|
// src/cli.ts
|
|
56981
|
-
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["h", "help", "summary", "reliable-only"]);
|
|
57856
|
+
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["h", "help", "summary", "reliable-only", "interactive", "no-interactive"]);
|
|
56982
57857
|
async function main() {
|
|
56983
57858
|
const args = parseArgs(process.argv.slice(2));
|
|
56984
57859
|
if (args.command === "--help" || args.command === "-h" || args.command === "help" || !args.command) {
|
|
@@ -57010,12 +57885,16 @@ async function main() {
|
|
|
57010
57885
|
printJson(runDeck(args));
|
|
57011
57886
|
return;
|
|
57012
57887
|
}
|
|
57888
|
+
if (args.command === "article") {
|
|
57889
|
+
printJson(runArticle(args));
|
|
57890
|
+
return;
|
|
57891
|
+
}
|
|
57013
57892
|
if (args.command === "query") {
|
|
57014
57893
|
printJson(runQuery(args));
|
|
57015
57894
|
return;
|
|
57016
57895
|
}
|
|
57017
57896
|
printJson(agentError("UNKNOWN_COMMAND", `Unknown command: ${args.command ?? "(none)"}`, {
|
|
57018
|
-
commands: ["profile", "validate", "catalog", "render", "deck", "query"]
|
|
57897
|
+
commands: ["profile", "validate", "catalog", "render", "deck", "article", "query"]
|
|
57019
57898
|
}));
|
|
57020
57899
|
process.exitCode = 1;
|
|
57021
57900
|
} catch (error51) {
|
|
@@ -57073,11 +57952,12 @@ function runRender(args) {
|
|
|
57073
57952
|
const validation = validateReportSpec(normalized, profile, formats);
|
|
57074
57953
|
if (isAgentError(validation)) return fail(validation);
|
|
57075
57954
|
const themeFlag = stringFlag(args, "theme");
|
|
57955
|
+
const interactive = args.flags["interactive"] === true ? true : args.flags["no-interactive"] === true ? false : void 0;
|
|
57076
57956
|
const written = [];
|
|
57077
57957
|
for (const format of formats) {
|
|
57078
57958
|
if (format === "html") {
|
|
57079
57959
|
const htmlPath = formatOutputPath(output, "html", formats.length > 1);
|
|
57080
|
-
writeOutput(htmlPath, renderStaticHtml(validation.value, profile, dataset.value.rows, themeFlag));
|
|
57960
|
+
writeOutput(htmlPath, renderStaticHtml(validation.value, profile, dataset.value.rows, themeFlag, { enabled: interactive }));
|
|
57081
57961
|
written.push(htmlPath);
|
|
57082
57962
|
} else if (format === "svg") {
|
|
57083
57963
|
const svgPath = formatOutputPath(output, "svg", formats.length > 1);
|
|
@@ -57132,6 +58012,47 @@ function runQuery(args) {
|
|
|
57132
58012
|
if (isAgentError(result)) return fail(result);
|
|
57133
58013
|
return { ok: true, value: result };
|
|
57134
58014
|
}
|
|
58015
|
+
function runArticle(args) {
|
|
58016
|
+
const file2 = args.positional[0];
|
|
58017
|
+
if (!file2) {
|
|
58018
|
+
return fail(agentError("MISSING_INPUT", "Usage: miao-viz article <file> --output <file> [--style editorial|executive|minimal] [--format html|json|markdown]"));
|
|
58019
|
+
}
|
|
58020
|
+
const output = requiredFlag(args, "output");
|
|
58021
|
+
if (isAgentError(output)) return fail(output);
|
|
58022
|
+
const styleFlag = stringFlag(args, "style");
|
|
58023
|
+
const style = parseArticleStyle(styleFlag);
|
|
58024
|
+
if (!style) {
|
|
58025
|
+
return fail(agentError("UNSUPPORTED_ARTICLE_STYLE", `Unsupported article style: ${styleFlag}`, {
|
|
58026
|
+
supportedStyles: ["editorial", "executive", "minimal"]
|
|
58027
|
+
}));
|
|
58028
|
+
}
|
|
58029
|
+
const formatFlag = stringFlag(args, "format");
|
|
58030
|
+
const format = parseArticleFormat(formatFlag);
|
|
58031
|
+
if (!format) {
|
|
58032
|
+
return fail(agentError("UNSUPPORTED_ARTICLE_FORMAT", `Unsupported article output format: ${formatFlag}`, {
|
|
58033
|
+
supportedFormats: ["html", "json", "markdown"]
|
|
58034
|
+
}));
|
|
58035
|
+
}
|
|
58036
|
+
const generated = generateInfographicFromFile(file2, style);
|
|
58037
|
+
if (isAgentError(generated)) return fail(generated);
|
|
58038
|
+
if (format === "json") {
|
|
58039
|
+
writeOutput(output, `${JSON.stringify(generated.value.spec, null, 2)}
|
|
58040
|
+
`);
|
|
58041
|
+
} else if (format === "markdown") {
|
|
58042
|
+
writeOutput(output, generated.value.markdown);
|
|
58043
|
+
} else {
|
|
58044
|
+
writeOutput(output, renderInfographicHtml(generated.value.spec));
|
|
58045
|
+
}
|
|
58046
|
+
return {
|
|
58047
|
+
ok: true,
|
|
58048
|
+
value: {
|
|
58049
|
+
output,
|
|
58050
|
+
format,
|
|
58051
|
+
style,
|
|
58052
|
+
sections: generated.value.spec.sections.map((section) => section.type)
|
|
58053
|
+
}
|
|
58054
|
+
};
|
|
58055
|
+
}
|
|
57135
58056
|
function normalizeSpec(spec) {
|
|
57136
58057
|
const parsed = singleOrReportSpecSchema.safeParse(spec);
|
|
57137
58058
|
if (!parsed.success) {
|
|
@@ -57147,12 +58068,12 @@ function parseFormats(value) {
|
|
|
57147
58068
|
}
|
|
57148
58069
|
}
|
|
57149
58070
|
function readSpec(file2) {
|
|
57150
|
-
const text = (0,
|
|
58071
|
+
const text = (0, import_node_fs3.readFileSync)(file2, "utf8");
|
|
57151
58072
|
if (file2.endsWith(".json")) return JSON.parse(text);
|
|
57152
58073
|
return YAML.parse(text);
|
|
57153
58074
|
}
|
|
57154
58075
|
function readJson(file2) {
|
|
57155
|
-
return JSON.parse((0,
|
|
58076
|
+
return JSON.parse((0, import_node_fs3.readFileSync)(file2, "utf8"));
|
|
57156
58077
|
}
|
|
57157
58078
|
function readProfile(file2) {
|
|
57158
58079
|
const parsed = readJson(file2);
|
|
@@ -57207,8 +58128,8 @@ function formatOutputPath(output, ext, multiple) {
|
|
|
57207
58128
|
return output;
|
|
57208
58129
|
}
|
|
57209
58130
|
function writeOutput(file2, content) {
|
|
57210
|
-
(0,
|
|
57211
|
-
(0,
|
|
58131
|
+
(0, import_node_fs3.mkdirSync)((0, import_node_path3.dirname)(file2), { recursive: true });
|
|
58132
|
+
(0, import_node_fs3.writeFileSync)(file2, content, "utf8");
|
|
57212
58133
|
}
|
|
57213
58134
|
function fail(error51) {
|
|
57214
58135
|
process.exitCode = 1;
|
|
@@ -57261,6 +58182,8 @@ Options:
|
|
|
57261
58182
|
--output <file> Output file path
|
|
57262
58183
|
--format <fmt> Output format: html, svg (default: html)
|
|
57263
58184
|
--theme <name> Theme: default, editorial, dark, minimal
|
|
58185
|
+
--interactive Force lightweight interactive runtime for HTML output
|
|
58186
|
+
--no-interactive Force static HTML output even when interaction spec exists
|
|
57264
58187
|
--sheet <name> Sheet name (Excel only)
|
|
57265
58188
|
--limit <n> Max rows to read
|
|
57266
58189
|
`,
|
|
@@ -57275,6 +58198,19 @@ Options:
|
|
|
57275
58198
|
--theme <name> Theme: default, editorial, dark, minimal
|
|
57276
58199
|
--sheet <name> Sheet name (Excel only)
|
|
57277
58200
|
--limit <n> Max rows to read
|
|
58201
|
+
`,
|
|
58202
|
+
article: `Usage: miao-viz article <file> --output <file> [options]
|
|
58203
|
+
|
|
58204
|
+
Convert a local Markdown or plain-text article into a static infographic artifact.
|
|
58205
|
+
URL fetching is intentionally handled by the agent/skill layer; this command only reads local files.
|
|
58206
|
+
|
|
58207
|
+
Arguments:
|
|
58208
|
+
file Path to a .md, .markdown, or .txt article file
|
|
58209
|
+
|
|
58210
|
+
Options:
|
|
58211
|
+
--output <file> Output file path
|
|
58212
|
+
--format <fmt> Output format: html, json, markdown (default: html)
|
|
58213
|
+
--style <name> Style: editorial, executive, minimal (default: editorial)
|
|
57278
58214
|
`,
|
|
57279
58215
|
query: `Usage: miao-viz query <file> [options]
|
|
57280
58216
|
|
|
@@ -57309,6 +58245,7 @@ Commands:
|
|
|
57309
58245
|
catalog List all available chart types
|
|
57310
58246
|
render Render a vizspec to HTML or SVG
|
|
57311
58247
|
deck Render a deck spec to HTML slides
|
|
58248
|
+
article Convert a local article to an infographic artifact
|
|
57312
58249
|
|
|
57313
58250
|
Run "miao-viz <command> --help" for command-specific options.
|
|
57314
58251
|
`);
|