Report folders, local data and output
Report folders, local data and output
Each rendered report requires its own named folder with exactly one .rmd directly inside it. Missing sources/roots, multiple .rmd files (including nested ones), unreadable inventories, escaped paths and external symlinks are hard failures. CLI commands exit nonzero; editor/service/browser hosts expose actionable structured diagnostics. Parsing, formatting and static suggestions stay available to repair invalid source. No host infers a report from its process working directory.
quarterly-sales/
report.rmd
report.rstyle.yaml # optional; referenced explicitly
data/sales.csv # optional larger input
media/logo.svg # optional local media
Rendered/ # generated; exact capital R
report.html
data/sales.csv # copied when used, including local links
media/logo.svg # copied when used; media is also embedded
report.rstyle.yaml # copied when referenced
Small authored charts/tables default to expanded row mappings inside the consuming block:
type: bar
title: Revenue increased from January to February
encoding: {x: month, y: revenue}
data:
inline:
- month: Jan
revenue: 100
- month: Feb
revenue: 140
# For larger inputs choose exactly one block-local file reference instead:
# data: {csv: data/sales.csv}
# data: {json: data/sales.json}
Header data and block data.id are retired and fail with replacement guidance. For portable report bundles, each block chooses exactly one of inline, csv or json; file references are relative, bounded paths inside the same report root. Native hosts check canonical paths including symlinks; browser/source-only hosts must supply a matching explicit report_folder: {root, source, files} inventory before resolving or rendering. Static browser bundles use a sibling report-folder.json, generated by the builder. In-memory playground drafts have one explicit isolated virtual folder and support inline/embedded resources only.
Configured native hosts also resolve data.sql, data.metricflow and data.cache through named connectors declared in report.connectors.yaml beside the report. The host must supply the connector’s runtime and configuration; a missing connector declaration is a resource-resolution failure. See examples/sql-report for a local DuckDB report. Credentials stay in trusted host configuration.
The accepted data.url field is not implemented by current native or browser loaders. Save remote input as a report-local CSV or JSON file for validation, rendering and portable exports.
Pure core performs no I/O. Hosts provide resolved CSV/JSON bytes in RenderOptions.data keyed by the exact local reference, with style sources/media supplied by their adapters. The source key decides the parser, never the content: csv: content is CSV and json: content is JSON, and content that does not parse as the declared format is data.csv or data.json (error) at the block’s line.
Data typing is one contract for inline, CSV and JSON rows in every block:
- CSV is UTF-8 with a dropped BOM, RFC 4180 quoting and a comma delimiter only; there is no
delimiteroption, and a semicolon file fails as ragged rows. A CSV or JSON file in another encoding, such as an Excel export in Windows-1250, is adata.csvordata.jsonerror at its first byte that is not UTF-8, and only the blocks that read it fail. Every header and value is trimmed. The header is the first row; names must be non-empty and unique, elsedata.csv.header. A row with a different number of fields isdata.csvand names the file line. An empty cell is null;trueandfalseare booleans; a number whose decimal value survives binary64 is a number (10.50becomes 10.5); a token with a leading zero (00123; not0or0.5) is a string identifier that sorts as text and is never totalled; an integer beyond 2^53 or a decimal that does not round-trip is numeric text that keeps its digits; anything else is a string. - JSON is an array of flat row objects whose values are strings, finite numbers, booleans or null. Any other top-level shape, including the former
{"inline": [...]}wrapper, or a nested object or array value isdata.json(error) naming the row and key. - Inline rows follow the JSON value rule: a row that is not a mapping, or a nested value, is one
data.inlineerror at that row (table.data.inline[1]), naming the row and key. - Missing values are null, an empty CSV cell, or
.,NA,N/A,NaN,nullandnonein any case after trimming. Charts draw a gap and count the rows in achart.missing-observationsinfo check. Tables print the cell as written, sort it after every value in both directions and leave it out of totals. Any other text in a numeric chart channel, including comma-grouped numbers such as1,234, ischart.non-numeric(error) naming the row and value; in a table it stays text. - Dates and times are strings only (a quoted year is temporal, an unquoted year a number).
YYYYis year precision,YYYY-MMmonth,YYYY-MM-DDday; a date followed byT,tor a space andHH:MMis minute precision, with:SSsecond precision and a fraction of 1 to 9 digits after.or,millisecond precision (digits beyond three are dropped). A date-time may end inZorz,±HH,±HHMMor±HH:MM; without an offset it is UTC. Surrounding spaces are ignored. Out-of-range fields (month 13, 30 February, 25:00, an offset of 24 hours) make the value a non-date. An axis is calendar only when everyxvalue qualifies. - Limits, enforced in core for every host: a
.rmdsource over 16 MiB issource.limit; one resolved dataset over 16 MiB isdata.limit; generated HTML over 64 MiB isrender.output-limit.
CSV numeric cells become JSON numbers only when their decimal value survives binary64 parsing and JSON number serialization. Longer decimals and integers that would lose digits, such as 1.234567890123456789 or 9007199254740993, remain source text; chart coordinates still use binary64 while readouts and source downloads retain those digits. Ordinary numeric typing and insignificant trailing-zero normalization are unchanged. JSON input cannot recover digits already rounded by its producer.
Native generate and VS Code RichView: Export to Rendered create Rendered/<source-basename>.html. -o name.html selects another basename there; an explicit output path must be directly inside that same Rendered directory. Source/style/data are preserved. Output directories/files cannot escape or be symlinks, and inputs cannot collide with output. CSS, licensed fonts, data/media and the Rust chart runtime are embedded in HTML; referenced local files are also copied under Rendered with matching relative paths. Missing interactive runtime refuses chart export with a build/package instruction. Browser downloads cannot select a filesystem directory: save their self-contained HTML inside your report’s Rendered; reports with local document links must use native/editor export for the copied bundle (browser download refuses them). Explicit external web links/embeds still depend on their remote destinations.
PDF output
richview generate report.rmd --to pdf writes Rendered/report.pdf atomically. A .pdf output filename also selects this mode. --page a4|letter defaults to A4; --browser PATH or RICHVIEW_BROWSER selects a local Chromium-family browser, otherwise discovery checks PATH and macOS application bundles. Linux and macOS are supported; Windows and hosted rendering are not. Chrome 131 or later provides running titles and page counters. --settle-timeout defaults to 30 seconds. MCP richview_render accepts output: report|pdf and page: a4|letter. Core/JavaScript RenderOptions.output returns print-ready HTML; filesystem export and browser execution belong to the native host.
PDF HTML carries data-rv-output="pdf" and chart payloads with output: "pdf" and motion.enabled: false. The driver emulates print/reduced motion at the page text width, awaits the runtime’s window.__richview.settled() hook (or fonts, chart mounts and final frames with older runtimes), then requests a tagged PDF with an outline. All browser requests except the staged report, data: resources and GET requests for the images of credited https://images.unsplash.com/ photo figures (image blocks and title_image) that the report shows are blocked (export.network-blocked). Data, styles and media retain the bounded report-folder contract; PDF mode allows no other remote resources.
The print stylesheet is present in every report. Sleek prints as Air, the light builtin its print-as token names; other styles keep their treatment. PDF page margins are 20 mm top, 18 mm sides and 22 mm bottom, with a running title and page / pages footer after the first page. Content keeps its source order; figures and their following source note, callouts, metrics and table rows avoid breaks. Tables print all rows, repeat headers and print totals once. Video becomes a poster plus caption/filename, or a diagnosed text card without a poster; audio and embeds become link cards; PDF HTML emits no players or iframe. Local links print as text. Images load eagerly, chart controls are hidden, and the existing RichView colophon follows the content. No outputs.* frontmatter, carousel or story output is implemented by this contract.
Whole HTTP(S) URL table cells link with the hostname as their screen text and the full address in href and title; print uses the full address. ISO dates and identifier-like cells keep their tokens on one line. Table payloads, sorting and CSV values retain the original source strings.
The former --allow-network flag is removed. Keep data inside the report folder or use a configured native connector.