Data types: files, documents, tables, links, dates and colors
Take uploaded files and structured data in a Block and return tables, documents, charts and downloads, with generated controls and checked conversions.
Ports can carry general files and structured data, not only media and text. Each type has a checked value shape, a generated control and a result layout, so a Block that declares them needs no custom view.
| Type | Value | Generated control | Default result layout | |||
|---|---|---|---|---|---|---|
file | {kind: "file", assetId, versionId, mimeType, url?, name?, bytes?}; mimeType is application/pdf, text/plain, text/csv, text/markdown, application/json or application/zip | file (upload) | download | |||
document | `{schemaVersion: 1, format: "markdown" \ | "plain", text, title?}`, up to 1,000,000 characters | richtext | document | ||
table | `{columns: [name], rows: [{name: text \ | number \ | boolean \ | null}]}`, up to 500 columns and 100,000 rows | grid | table (with CSV download) |
url | An absolute http or https address without credentials | url | a link | |||
date | ISO 8601: 2026-10-09 or 2026-10-09T14:30 (seconds and an offset optional) | date (time: true for date and time) | a formatted date | |||
color | #rrggbb or #rrggbbaa | color | a swatch |
Lists of these types (file[], url[], date[], color[]) work like other lists; the list control edits text[], number[], integer[], url[], date[] and color[] item by item.
Output presentations in manifest.ui choose a layout for an output: {control: "table", port}, {control: "document", port} (a document or
Markdown text), {control: "chart", port, chart: "bar" | "line", x, y} (a table with optional column names, or a number[]), {control: "code", port, language} (text, json or object), {control: "player", port} (audio or video) and {control: "download", port} (file, file[] or file-bundle). Each control is checked against its port's type when the manifest is validated. Layouts show Block data as text: links in a document are displayed, not followed, and downloads go through the host's export policy.
Files
People upload files to a file input from the Block's controls. The host checks the extension, the declared type, the size (50 MB) and the bytes (a PDF or ZIP signature, valid UTF-8 text, parseable JSON) before the Block receives a saved reference. Module Blocks read the bytes with stillmade.files.read(input.script) and save new files with stillmade.files.write(bytes, {mediaType: "text/csv", name: "shots.csv"}), which returns a file value. Files a person uploaded to a Block, or a Block saved, are indexed in project context as files (file[], permission context.files.read), apart from media so media lists stay media.
Helpers
@stillmade/block-sdk/data (also exported from the main entry) has the checks and builders: isFileValue, isDocumentValue, isTableValue, isUrlValue, isDateValue, isColorValue, createTable(columns, rows), createDocument(text, {format, title}), tableToCsv(table), fileFormatFor(name), checkFileSignature(bytes, mimeType), and the DATA_VALUE_LIMITS, FILE_FORMATS, FILE_MEDIA_TYPES and DOCUMENT_FORMATS tables. IMPORT_FORMATS lists every upload the host stores (media and files), and inspectMediaImportFile(file, {kinds: ["file"]}) checks a chosen file.
Conversions
Connections convert between these types with host-owned conversions, version 1:
| Conversion | From | To |
|---|---|---|
text-to-url, url-to-text | text | url (checked), and back |
text-to-table, table-to-text | CSV text | table, and back to CSV |
object-to-table, table-to-object | an object shaped as a table (for example from csv-to-table) | table, and back |
text-to-document, document-to-text | text | a plain document, and back |
shots-to-shot-plan | shot[] | the Shots workspace's version 1 shot-plan |
A shot list reaches the Shots workspace when the output declares the workspace's meaning: {"type": "shot[]", "semantic": "production_shot_plan"}.
Example: PDF to shot list
examples/module-pdf-shot-list is a module Block that takes an uploaded PDF, reads its text with pdf.js (bundled, its worker code imported directly so nothing is fetched), plans the shots with one text.generate call that has a JSON schema, and returns a table and a shot[] the Shots workspace accepts.