headless

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 30, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package headless works on workbook files without the screen, for 012 get, set, recalc and export (see docs/files/scripts.md): it opens a .012 file, finds the cells a reference names, reads and writes them, lists the errors formulas show and saves atomically. Nothing here runs a program or reaches the network unless its caller asks: notebook cells run through RunNotebooks and JEV functions through AnswerJEV, which the command line calls only behind flags.

Index

Constants

This section is empty.

Variables

View Source
var Formats = []string{"text", "csv", "tsv", "json", "nuon"}

Formats are what Get writes: text as the sheet shows it, or a table as 012 --pipe and exports write it.

Functions

func AddPivot added in v0.4.0

func AddPivot(w *sheet.Workbook, spec PivotSpec) (string, error)

AddPivot adds a pivot table's sheet after its source's, returning its name.

func AnswerJEV

func AnswerJEV(ctx context.Context, w *sheet.Workbook, client jev.Client, timeout time.Duration) int

AnswerJEV answers w's JEV functions with client, asking what the workbook's formulas ask, a few questions at a time, until none is waiting, each within timeout. It returns how many were asked.

func AnswerJEVFrom added in v0.4.0

func AnswerJEVFrom(ctx context.Context, w *sheet.Workbook, client jev.Client, cache *jev.Cache, timeout time.Duration) int

AnswerJEVFrom is AnswerJEV with the answers kept in cache, which a caller opening the workbook again and again (012 mcp) keeps, so each question is asked once.

func Apply added in v0.4.0

func Apply(w *sheet.Workbook, ops []Operation, o SetOptions) (warnings []string, err error)

Apply makes ops as one change, in order, stopping at the first that fails with an error naming it; the workbook should then be thrown away rather than saved, as after Set.

func ChartTitle added in v0.4.0

func ChartTitle(c sheet.Chart) string

ChartTitle is a chart's title, or its type's name when it has none.

func Encode added in v0.4.0

func Encode(w io.Writer, format string, v any) error

Encode writes v, a value of this package's result types, as JSON (indented, one value) or NUON, the forms --format json and nuon ask for. NUON is the JSON read as nushell reads it, so both carry the same schema (docs/reference/json.md).

func Filter added in v0.4.0

func Filter(w *sheet.Workbook, ref string, cols []FilterColumn, remove bool) error

Filter puts a filter on the range ref names (its first row the header) with the columns' criteria, replacing the sheet's filter, as Data > Create a filter does; remove takes the sheet's filter off.

func FindChart added in v0.4.0

func FindChart(s *sheet.Sheet, which string) (int, error)

FindChart finds a chart of s by its number, 1 for the first in the order the sheet keeps them, or by its title, ignoring case.

func Get

func Get(out io.Writer, t Target, o GetOptions) error

Get writes the target's values. One cell is written alone: as it shows, or as a JSON or NUON value typed by its format (a number, a date, a file size). A range or sheet is written as a table: aligned columns as text, or as CSV, TSV, JSON or NUON, whose columns take their names from the first row (see GetOptions.NoHeader).

func MissingOutputs

func MissingOutputs(s *sheet.Sheet) []string

MissingOutputs names the notebook outputs sent to s that show no rows because the file holds no output for their cells: never run, too large to save, or their cells gone.

func ResolveCell

func ResolveCell(w *sheet.Workbook, ref string) (*sheet.Sheet, sheet.Addr, error)

ResolveCell is Resolve for a reference that must be one cell.

func RunNotebooks

func RunNotebooks(ctx context.Context, w *sheet.Workbook, o NotebookOptions) []string

RunNotebooks runs the code cells of each notebook tab, each after the cells it reads, as Run all does on the screen: each cell's output replaces the one the file kept and goes on to the sheet it was sent to. A failure stops its notebook's run, as on the screen. It returns a line for each cell that failed or couldn't run.

func Set

func Set(w *sheet.Workbook, entries []Entry, o SetOptions) (warnings []string, err error)

Set types each entry into its cell, in order, as one change. An entry a cell can't take (a formula that doesn't parse, a validation rule that rejects it, part of an array, a pivot table or a region, a protected range without Force) stops it with an error naming the cell, and the workbook should then be thrown away rather than saved. Entries a rule would only mark invalid are set, and returned as warnings.

func Sort added in v0.4.0

func Sort(w *sheet.Workbook, ref string, keys []SortKey, header bool) error

Sort sorts the rows of the range ref names by keys, as Data > Sort range does, leaving its first row in place when header is set.

func SyncOutputs

func SyncOutputs(w *sheet.Workbook)

SyncOutputs sends the outputs a file kept to the sheets they were sent to, as opening the file on the screen does: reading, not running.

func Trusted

func Trusted(w *sheet.Workbook, machine string) bool

Trusted reports whether the workbook's commands may run here without asking, by the rule macros follow: they were made or trusted on this computer, whose id is machine.

func WriteAtomic

func WriteAtomic(path string, mode fs.FileMode, write func(io.Writer) error) error

WriteAtomic writes a file with write into a temporary file of its own beside path, then renames it over path with mode.

func WriteDescription added in v0.4.0

func WriteDescription(w io.Writer, d Description) error

WriteDescription writes d as text: a line for each sheet saying what it holds, with what's on it indented below, then the named ranges.

Types

type CellRun added in v0.4.0

type CellRun struct {
	Notebook string `json:"notebook"`
	Cell     int    `json:"cell"`
	Name     string `json:"name"`
	State    string `json:"state"` // "ran" or "failed"
	Error    string `json:"error"`
	Output   string `json:"output"` // NUON
	Cut      bool   `json:"cut"`    // Output was longer and is cut
}

CellRun is what running a notebook cell gave: its state, its output as NUON (cut at maxShownOutput), or why it failed.

func RunCell added in v0.4.0

func RunCell(ctx context.Context, w *sheet.Workbook, name string, n int, o NotebookOptions) (CellRun, error)

RunCell runs cell n (1 for the first) of the notebook tab named, as Run does on the screen: its output replaces the one kept and goes on to the sheet it was sent to. Whether it may run at all (trust, the shell setting) is the caller's to decide first.

type Change added in v0.4.0

type Change struct {
	Kind  string          `json:"kind"`
	Sheet string          `json:"sheet"`
	Item  string          `json:"item"`
	Field string          `json:"field"`
	Old   json.RawMessage `json:"old"`
	New   json.RawMessage `json:"new"`
}

Change is one change as 012 diff --format json lists it: what kind of thing changed (cell, sheet, region, name, macro or a layout field), on which sheet, which item, which field of it, and its old and new values, null where it wasn't or isn't there.

func Changes added in v0.4.0

func Changes(changes []diff.Change) []Change

Changes are diff's changes as records.

type ChartDescription added in v0.4.0

type ChartDescription struct {
	Number int    `json:"number"`
	Title  string `json:"title"`
	Type   string `json:"type"`
	Data   string `json:"data"`
	At     string `json:"at"`
}

ChartDescription is a chart: Number is what 012 export --chart and the MCP tools call it.

type ChartMade added in v0.4.0

type ChartMade struct {
	Sheet  string `json:"sheet"`
	Number int    `json:"number"`
	Title  string `json:"title"`
}

ChartMade says which chart AddChart made: its sheet and number, as describe lists it.

func AddChart added in v0.4.0

func AddChart(w *sheet.Workbook, spec ChartSpec) (ChartMade, error)

AddChart adds a chart as spec says.

type ChartSpec added in v0.4.0

type ChartSpec struct {
	Data   string `json:"data" jsonschema:"the range to chart, e.g. Q3!A1:C9"`
	Type   string `json:"type,omitempty" jsonschema:"column, bar, line, pie, area or scatter; column when empty"`
	Title  string `json:"title,omitempty" jsonschema:"the title; guessed from the series when empty"`
	At     string `json:"at,omitempty" jsonschema:"the cell under the chart's top-left corner; right of the data when empty"`
	Width  int    `json:"width,omitempty" jsonschema:"the width in terminal cells, 20 to 240; 60 when 0"`
	Height int    `json:"height,omitempty" jsonschema:"the height in terminal cells, 8 to 120; 18 when 0"`
	ByRow  bool   `json:"by_row,omitempty" jsonschema:"series run along rows rather than down columns"`
	Stack  string `json:"stack,omitempty" jsonschema:"stacked or percent, for column, bar and area charts"`
}

ChartSpec is a chart to add, as Insert > Chart makes one: over Data, with the header row, category labels and title guessed as Sheets guesses them unless given.

type Description added in v0.4.0

type Description struct {
	Sheets []SheetDescription `json:"sheets"`
	Names  []NameDescription  `json:"names"`
}

Description is what a workbook holds, for 012 describe and the MCP describe tool: enough for agents (or people) to know which references to read and write without reading every cell. Its JSON form is a stable schema (docs/reference/json.md): fields are only added, never renamed or removed.

func Describe added in v0.4.0

func Describe(w *sheet.Workbook) Description

Describe describes the workbook as it is.

type Entry

type Entry struct {
	Ref   string `json:"ref" jsonschema:"one cell, as formulas write it: B7, Q3!B7, 'Q3 plan'!B7"`
	Input string `json:"input" jsonschema:"what to type, in en-US form: 1.5, =SUM(A1:A6), $1,200, 12%, 2026-09-29; empty clears the cell"`
}

Entry is one cell to set: a reference to one cell, and what to type in it, as the file stores entries (numbers and dates in en-US's form, formulas with commas, whatever the workbook's locale); "" clears it.

type Evaluated added in v0.4.0

type Evaluated struct {
	Cell  string          `json:"cell"`  // where it was computed
	Value json.RawMessage `json:"value"` // typed as ReadRange types values
	Text  string          `json:"text"`  // as the cell would show it
	Error string          `json:"error"` // why, when Value is an error
	Spill *Range          `json:"spill,omitempty"`
}

Evaluated is a formula's result, computed without keeping it.

func Evaluate added in v0.4.0

func Evaluate(w *sheet.Workbook, formula, at string) (Evaluated, error)

Evaluate computes formula as if typed in the cell at names (a cell below the shown sheet's data when ""), and returns what it shows. It changes w: call it on a workbook to throw away.

type ExportResult added in v0.4.0

type ExportResult struct {
	File   string   `json:"file"`
	Format string   `json:"format"`
	Rows   int      `json:"rows"`
	Notes  []string `json:"notes"`
}

ExportResult is what 012 export --report writes.

type File

type File struct {
	Path string
	Book *sheet.Workbook
	// New is set when there was no file at Path and Book is empty.
	New bool
	// contains filtered or unexported fields
}

File is a workbook opened from a .012 file.

func Open

func Open(path string, create bool) (*File, error)

Open reads the workbook at path, with the notebook outputs it kept sent on to their sheets, as the screen opens it. With create, a path with no file opens as an empty workbook that Save writes there, as opening a new name in 012 does.

func (*File) Changes added in v0.4.0

func (f *File) Changes() ([]diff.Change, error)

Changes compares the workbook as it is with its file as it was opened (nothing, for a new file), before it's saved.

func (*File) Save

func (f *File) Save() error

Save writes the workbook back to its file atomically: into a temporary file beside it, renamed over it once complete, so a failed write never leaves half a workbook. A file that would come out the same isn't touched, so its modification time says when it last changed.

type FilterColumn added in v0.4.0

type FilterColumn struct {
	Column    string   `json:"column" jsonschema:"a column letter (B) or the header's text"`
	Condition string   `` /* 139-byte string literal not displayed */
	Value     string   `json:"value,omitempty" jsonschema:"what the condition compares with, as typed: 100, 2026-01-31, North"`
	Hide      []string `json:"hide,omitempty" jsonschema:"values to hide, as the cells show them"`
}

FilterColumn is one column's criteria for Filter: a condition (as Data > Create a filter's "Filter by condition" names them) with its value, and values to hide ("Filter by values").

type FindOptions added in v0.4.0

type FindOptions struct {
	Ref        string `json:"ref,omitempty" jsonschema:"a sheet or range to search; every sheet when empty"`
	MatchCase  bool   `json:"match_case,omitempty"`
	WholeCell  bool   `json:"whole_cell,omitempty" jsonschema:"the whole cell must match"`
	Regex      bool   `json:"regex,omitempty" jsonschema:"the query is a regular expression"`
	InFormulas bool   `json:"in_formulas,omitempty" jsonschema:"search formulas' text rather than their values"`
	Limit      int    `json:"limit,omitempty" jsonschema:"the most matches to return; 100 when 0"`
}

FindOptions tune Find.

type Found added in v0.4.0

type Found struct {
	Matches []Match `json:"matches"`
	Total   int     `json:"total"`
}

Found is what Find returns: the first matches, and how many there are in all.

func Find added in v0.4.0

func Find(w *sheet.Workbook, query string, o FindOptions) (Found, error)

Find searches the workbook's sheets, or the sheet or range o.Ref names, as Edit > Find and replace does.

type GetOptions

type GetOptions struct {
	Format string // one of Formats; "" is text
	// Input writes what was typed (a formula, or an entry as stored)
	// rather than the value.
	Input bool
	// NoHeader names a JSON or NUON table's columns by their letters and
	// makes every row a record, rather than taking the names from the
	// first row.
	NoHeader bool
}

GetOptions say how Get writes.

type Match added in v0.4.0

type Match struct {
	Cell  string `json:"cell"`
	Text  string `json:"text"`
	Input string `json:"input"`
}

Match is a cell Find found: where, what it shows and what was typed.

type NameDescription added in v0.4.0

type NameDescription struct {
	Name  string `json:"name"`
	Range string `json:"range"` // with its sheet: Q3!B2:B9, or #REF!
}

NameDescription is a named range.

type NotebookCell added in v0.4.0

type NotebookCell struct {
	Number int    `json:"number"`
	Kind   string `json:"kind"` // "code" or "note"
	Name   string `json:"name,omitempty"`
	Source string `json:"source"`
	// State is "ran", "failed" or "not run"; Error says why it failed.
	State string `json:"state,omitempty"`
	Error string `json:"error,omitempty"`
}

NotebookCell is one cell of a notebook tab.

type NotebookOptions

type NotebookOptions struct {
	Runner   nushell.Runner // nu, or a fake in tests
	Timeout  time.Duration  // for each cell
	NuConfig bool           // run nu with the user's config files
}

NotebookOptions say how RunNotebooks runs cells.

type Operation added in v0.4.0

type Operation struct {
	Op     string    `` /* 151-byte string literal not displayed */
	Ref    string    `` /* 135-byte string literal not displayed */
	Input  string    `` /* 126-byte string literal not displayed */
	Count  int       `json:"count,omitempty" jsonschema:"for insert_rows and insert_columns: how many; 0 inserts as many as ref spans"`
	Name   string    `json:"name,omitempty" jsonschema:"for add_sheet and rename_sheet the sheet's name, for define_name the range's"`
	Keys   []SortKey `json:"keys,omitempty" jsonschema:"for sort: the columns to sort by, first first"`
	Header bool      `json:"header,omitempty" jsonschema:"for sort: the range's first row is a header and stays in place"`
}

Operation is one change of an Apply: what the MCP apply_operations tool takes, each the same engine call a menu command makes. Op says which, and the other fields are its arguments:

set             Ref (one cell) and Input, as 012 set types it
clear           Ref: the range's contents, keeping formats and notes
insert_rows     before Ref's first row, Count rows (Ref's rows when 0)
delete_rows     Ref's rows
insert_columns  before Ref's first column, Count columns
delete_columns  Ref's columns
add_sheet       Name, after the last sheet
rename_sheet    Ref (a sheet) to Name
delete_sheet    Ref (a sheet)
define_name     Name for the range Ref
sort            Ref's rows by Keys, the first row left in place with Header

type PivotDescription added in v0.4.0

type PivotDescription struct {
	Source string `json:"source"` // Sheet!A1:D99
}

PivotDescription is where a pivot table's data comes from.

type PivotSpec added in v0.4.0

type PivotSpec struct {
	Source  string       `json:"source" jsonschema:"the data, header row included, e.g. Sales!A1:F99"`
	Name    string       `json:"name,omitempty" jsonschema:"the new sheet's name; Pivot Table N when empty"`
	Rows    []string     `json:"rows,omitempty" jsonschema:"columns whose values become rows, by header or letter"`
	Columns []string     `json:"columns,omitempty" jsonschema:"columns whose values become columns, by header or letter"`
	Values  []PivotValue `json:"values" jsonschema:"columns to summarize"`
	Totals  bool         `json:"totals,omitempty" jsonschema:"add grand totals"`
}

PivotSpec is a pivot table to add, as Insert > Pivot table makes one: a new sheet summarizing Source, whose first row names its columns.

type PivotValue added in v0.4.0

type PivotValue struct {
	Column    string `json:"column" jsonschema:"by header or letter"`
	Summarize string `json:"summarize,omitempty" jsonschema:"sum, counta, count, countunique, average, max or min; sum when empty"`
}

PivotValue is a column to summarize, and how.

type Problem

type Problem struct {
	Sheet string
	Addr  sheet.Addr
	Value string // the error as the cell shows it: #DIV/0!
	Why   string // what the screen's context line says about it, or ""
	// JEV is set when the error is a JEV function's with nothing to
	// answer it: --jev asks the model.
	JEV bool
}

Problem is a cell whose formula shows an error after recalculating.

func Errors

func Errors(w *sheet.Workbook) []Problem

Errors returns the cells of w showing errors, without recalculating. A cell an array spilled an error into is its formula's problem, so only formulas are listed.

func Recalc

func Recalc(w *sheet.Workbook) (problems []Problem, circular bool)

Recalc recomputes every formula of w and returns the cells showing errors, sheet by sheet in tab order and row by row, and whether a circular reference was found.

func (Problem) At

func (p Problem) At() string

At is the problem's cell with its sheet: Q3!B7.

type ProblemRecord added in v0.4.0

type ProblemRecord struct {
	Cell  string `json:"cell"` // with its sheet: Q3!B7
	Sheet string `json:"sheet"`
	Addr  string `json:"addr"`
	Value string `json:"value"` // #DIV/0!
	Why   string `json:"why"`
	JEV   bool   `json:"jev"` // a JEV function's, answered with --jev
}

ProblemRecord is a formula showing an error.

func Problems added in v0.4.0

func Problems(ps []Problem) []ProblemRecord

Problems are problems as records.

type Range added in v0.4.0

type Range struct {
	// Range is what was read, with its sheet: Q3!A1:D9; a whole sheet is
	// read from A1 to its last cell with contents.
	Range string `json:"range"`
	Rows  int    `json:"rows"`
	Cols  int    `json:"cols"`
	// Values are the cells row by row, typed as 012 get --format json
	// types one cell: numbers, strings, true or false, null for blank,
	// dates as ISO 8601 strings, errors as their text (#DIV/0!).
	Values [][]json.RawMessage `json:"values"`
	// Text is each cell as the sheet shows it, when asked for.
	Text [][]string `json:"text,omitempty"`
	// Formulas are the formulas in the range by cell (B7: =SUM(B1:B6)).
	Formulas map[string]string `json:"formulas"`
	// Truncated is set when the range held more than the most cells a
	// read returns; Rows says how many rows came back.
	Truncated bool `json:"truncated"`
}

Range is a range's values as a grid, for agents: what ReadRange returns and the MCP read_range tool gives back. Its JSON form is a stable schema (docs/reference/json.md).

func ReadRange added in v0.4.0

func ReadRange(t Target, o ReadOptions) Range

ReadRange reads t's cells as a grid.

type ReadOptions added in v0.4.0

type ReadOptions struct {
	MaxCells int  // the most cells to return, whole rows at a time; 0 is 10,000
	Text     bool // also return each cell as shown
}

ReadOptions tune ReadRange.

type RecalcResult added in v0.4.0

type RecalcResult struct {
	File     string          `json:"file"`
	Errors   []ProblemRecord `json:"errors"`
	Circular bool            `json:"circular"`
	// NotebookFailures are the notebook cells --notebooks couldn't run.
	NotebookFailures []string `json:"notebook_failures"`
}

RecalcResult is what 012 recalc writes.

type RegionDescription added in v0.4.0

type RegionDescription struct {
	Name  string `json:"name"`
	Kind  string `json:"kind"` // "output" or "linked"
	Range string `json:"range"`
	File  string `json:"file,omitempty"` // a linked region's file
}

RegionDescription is a linked file or a notebook cell's output sent to the sheet.

type SetOptions

type SetOptions struct {
	// Force sets cells in protected ranges, which are refused otherwise,
	// as the screen asks before editing them.
	Force bool
}

SetOptions tune Set.

type SetResult added in v0.4.0

type SetResult struct {
	File     string   `json:"file"`
	Saved    bool     `json:"saved"`
	Changes  []Change `json:"changes"`
	Warnings []string `json:"warnings"`
}

SetResult is what 012 set writes: the file, whether it was saved (not with --dry-run), what changed, and the warnings of entries a rule marks invalid.

type SheetDescription added in v0.4.0

type SheetDescription struct {
	Name   string `json:"name"`
	Kind   string `json:"kind"` // "sheet", "notebook" or "pivot"
	Shown  bool   `json:"shown"`
	Hidden bool   `json:"hidden"`
	// Used is the range from A1 to the last cell with contents, "" when
	// the sheet is empty; Rows and Cols are its size.
	Used     string `json:"used"`
	Rows     int    `json:"rows"`
	Cols     int    `json:"cols"`
	Cells    int    `json:"cells"`
	Formulas int    `json:"formulas"` // -1 on sheets too large to count them
	Errors   int    `json:"errors"`   // formulas showing errors; -1 when not counted
	// Header is the row guessed to name the used range's columns, and
	// Columns its names; 0 and none when no row looks like one.
	Header   int                 `json:"header_row"`
	Columns  []string            `json:"columns"`
	Frozen   [2]int              `json:"frozen"` // rows, columns
	Filter   string              `json:"filter"` // the filtered range, or ""
	Tables   []TableDescription  `json:"tables"`
	Regions  []RegionDescription `json:"regions"`
	Charts   []ChartDescription  `json:"charts"`
	Pivot    *PivotDescription   `json:"pivot,omitempty"`
	Notebook []NotebookCell      `json:"notebook_cells,omitempty"`
}

SheetDescription is one tab: a sheet or a notebook.

type SortKey added in v0.4.0

type SortKey struct {
	Column     string `json:"column" jsonschema:"a column letter (B) or the header's text"`
	Descending bool   `json:"descending,omitempty"`
}

SortKey is a column to sort by: a letter (B) or, with a header, the header's text.

type TableDescription added in v0.4.0

type TableDescription struct {
	Name    string   `json:"name"`
	Range   string   `json:"range"` // header row included
	Columns []string `json:"columns"`
}

TableDescription is a named table.

type Target

type Target struct {
	Sheet *sheet.Sheet
	Range sheet.Rect
	Whole bool
}

Target is what a reference names: a range of one sheet, or the whole sheet (from A1 to its last cell with contents).

func Resolve

func Resolve(w *sheet.Workbook, ref string) (Target, error)

Resolve finds what ref names in w: a cell or range (B7, A1:C9, A:A), on a sheet (Q3!B7, 'Q3 plan'!A1:C9) or else the sheet shown when the file was saved; a named range (Sales); a table by name, all of it with its header row, or by a structured reference as formulas read it (Sales[Amount]); or a sheet by name (Q3, or Q3!), meaning all of it. "" is the whole sheet shown.

func (Target) Cell

func (t Target) Cell() bool

Cell reports whether the target is one cell.

func (Target) String

func (t Target) String() string

String names the target as a formula would, with its sheet.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL