Help / The view file
The view file
A CSV cannot hold a calculation, a colour or a hidden row. Plaintable keeps those in a markdown file beside the data — one you can read, edit by hand, diff and commit. This is everything you can write in it.
Where the file lives
The view file is the CSV's whole file name plus .md, in the same folder:
sales.csv the data
sales.csv.md how Plaintable shows it The full name is used deliberately, so a sales.md you wrote yourself is never mistaken for it.
Plaintable creates the file the first time you change something about the view, and rewrites it whenever the view changes after that. It also watches it: save the file from another editor and the open window follows along within a moment. Nothing written in the view file ever changes the CSV — calculated columns are shown in the table and left out of the file entirely.
Folder access. Plaintable is sandboxed. Opening sales.csv does not by itself grant access to sales.csv.md, because the sandbox's related-item rule only covers a file with the same base name. The first time a view file is needed, macOS asks for access to the containing folder; Plaintable keeps a bookmark and does not ask again. Decline it and the table still works — only the view file is disabled.
The shape of the file
It is ordinary markdown. A # title, then a ## heading per section, and inside most sections either a pipe table or a short list of - Key: value lines.
# sales.csv
## Columns
| Column | Show | Pin | Width | Align | Format | Formula |
| ------ | ---- | --- | ----- | ----- | ------------- | ---------------------------- |
| Order | yes | yes | 115 | left | | |
| Units | yes | no | 85 | right | number | |
| Price | yes | no | 110 | right | currency(EUR) | |
| Total | yes | no | 140 | right | currency(EUR) | `[Units] * [Price]` |
## Rows
- Hidden: 6, 40-44
## Sort
1. Total — descendingThe sections Plaintable understands, in the order it writes them:
| Heading | Also accepted | Holds |
|---|---|---|
## Columns | — | Order, visibility, pinning, width, alignment, format, formula |
## Rows | — | Hidden rows and per-row heights |
## Row formatting | Row styles, Formatting | Conditional row colouring |
## Cell formulas | Cells | Formulas pinned to one cell |
## Sort | Sorting | Sort keys, in priority order |
## Filters | Filter | The active filter set |
## Saved filters | — | Named filter sets |
## Formula reference | — | A cheat sheet Plaintable writes for you. Ignored on read. |
Heading names are matched case-insensitively, and the order of sections in the file does not matter. Any ## section Plaintable does not recognise is kept exactly as written and re-emitted at the end, so you can add notes of your own. Text before the first ## heading is the generated preamble and will be replaced.
In a table, only the row order and the column positions matter — the header row is read for its shape, not its words, and the | --- | separator line is optional. Leading and trailing pipes may be left off. A literal pipe inside a cell is written \|. Backticks around a formula are optional; they are stripped on read and added back when Plaintable rewrites the file.
Columns
Seven cells per row. Columns appear in the table in the order they are listed here.
| Column | Show | Pin | Width | Align | Format | Formula |
| -------- | ---- | --- | ----- | ----- | ------------- | -------------------------------------- |
| Order | yes | yes | 115 | left | | |
| Date | yes | no | 105 | left | date(d MMM) | |
| Category | no | no | 100 | left | | |
| Units | yes | no | 85 | right | number | |
| Price | yes | no | 110 | right | currency(EUR) | |
| Discount | yes | no | 110 | right | percent(0) | |
| Revenue | yes | no | 140 | right | currency(EUR) | `[Units] * [Price] * (1 - [Discount])` || Cell | Values | Meaning |
|---|---|---|
| Column | a header name | Must match a header in the CSV — unless it also has a Formula, in which case it is a new calculated column. Required; a row with an empty name is skipped. |
| Show | yes no | Hides the column without touching the CSV. Default yes. |
| Pin | yes no | Freezes the column to the left so it stays put while you scroll sideways. Default no. |
| Width | a number, or auto | Width in points. Anything that is not a number — auto, or blank — lets Plaintable pick. |
| Align | left center right | l/start, centre/c/middle and r/end also work. Blank or unrecognised means automatic: numbers right, everything else left. |
| Format | see Formats | Presentation only. Sorting, filtering and saving always see the raw value. |
| Formula | an expression | Makes the column calculated. It is shown in the table and never written to the CSV. See the formula language. |
Yes and no are generous: yes, y, true, t and 1 all mean yes; no, n, false, f and 0 all mean no. Anything else falls back to the default.
You may leave trailing cells off a row entirely. | Notes | no | is a valid way to hide a column and say nothing else about it.
Formats
A format changes how a value is drawn, never what is stored. A value the format cannot make sense of is shown as it is, so a stray n/a in a number column stays n/a.
| Format | Example | Result |
|---|---|---|
| (blank) | | Automatic — the value exactly as stored. |
text | text | Never interpreted as anything but text. string is a synonym. |
number | number · number(2) | Grouped thousands. The argument is the number of decimals; omit it to keep the value's own. |
plain | plain · plain(0) | The same, without the thousands separator. |
percent | percent(0) | 0.15 → 15%. The argument is the number of decimals; the default is 1. |
currency | currency(EUR) · currency(USD, 0) | Currency code, then optionally the number of decimals. The default code is USD; without a decimal count the currency's own is used. |
date | date(d MMM yyyy) | A date pattern. datetime is a synonym; with no argument the pattern is yyyy-MM-dd. |
Format names are case-insensitive; arguments are not. Date patterns keep their case exactly as you type them, which matters — MM is the month and mm is the minute. Currency codes are upper-cased for you. A format Plaintable cannot read falls back to automatic rather than erroring, so a typo hides nothing.
Common date patterns:
| Pattern | 2026-01-06 becomes |
|---|---|
date(yyyy-MM-dd) | 2026-01-06 |
date(d MMM) | 6 Jan |
date(d MMMM yyyy) | 6 January 2026 |
date(EEE, d MMM) | Tue, 6 Jan |
date(MM/dd/yy) | 01/06/26 |
Rows
Two bullets, both optional. Line numbers are the ones in the CSV, counting the header as line 1 — the same numbers the gutter shows.
## Rows
- Hidden: 6, 40-44, 512
- Heights: 12 = 60, 13 = 44- Hidden — rows to leave out of the table. Single line numbers and
low-highranges, separated by commas, spaces or semicolons. Hidden rows are kept apart from the filters, so clearing the filters does not bring them back. Aliases:Hidden rows,Hide. - Heights — an explicit row height in points, as
line = heightpairs separated by commas. Aliases:Row heights,Height.
Row formatting
A table of when and style. The condition is a formula read as a yes/no and evaluated per row.
## Row formatting
| When | Style |
| --------------------------- | --------------------------- |
| `[Discount] >= 0.2` | background amber |
| `[Revenue] > 9000` | bold |
| `[Status] = "Refunded"` | strikethrough, text red |
| `ISEMPTY([Rep])` | off — italic, text gray |Every rule that matches applies, top to bottom, so a later rule layers over an earlier one rather than replacing it. A row that is both discounted and large comes out bold on amber without the two rules knowing about each other.
Styles
A comma-separated list, in any order:
| Style | Also accepted |
|---|---|
bold | — |
italic | — |
strikethrough | strike, struck |
background <colour> | bg, fill, highlight |
text <colour> | color, colour, foreground |
<colour> | a colour on its own means background |
Prefix the style with off — to keep a rule in the file but switch it off. off -, off, and off: work too.
Colours
Either one of the built-in names, or a hex value: #RGB, #RRGGBB, or #RRGGBBAA with an alpha channel.
| Name | Hex | Name | Hex |
|---|---|---|---|
red | #E5484D | teal | #12A594 |
orange | #F76B15 | blue | #3E63DD |
amber | #FFB224 | purple | #8E4EC6 |
yellow | #F5D90A | pink | #D6409F |
green | #30A46C | gray / grey | #8B8D98 |
black | #000000 | white | #FFFFFF |
Cell formulas
A calculation that overrides one single cell, addressed by CSV line number and column name. This is what typing = into a cell writes.
## Cell formulas
| Line | Column | Formula |
| ---- | ------- | ------------------------ |
| 41 | Revenue | `SUM([Units]) * 12.5` |
| 87 | Notes | `"checked " & TODAY()` |All three cells are required; a row missing any of them is skipped.
Sort
A list, in priority order: the first entry is the primary key, the second breaks its ties, and so on.
## Sort
1. Region — ascending
2. Revenue — descending Numbered lists, - bullets and bare lines all work. The direction may be separated by an em dash, an en dash, a hyphen, a colon or a comma. Anything starting with desc, z or ↓ means descending; anything else, including a missing direction, means ascending.
Comparison is type-aware — numeric, chronological, boolean, or localised natural-order text — and stable, so equal rows keep their file order. Empty values are pinned to one end regardless of direction; which end is a preference in Settings.
Filters
A few options as bullets, then a table of one filter per row. A row is shown when the filters agree, according to Match.
## Filters
- Match: all
- Search: keyboard
| Column | Test | Value |
| ------- | ------------- | ----------------- |
| Region | is one of | East, North, West |
| Revenue | at least | 1500 |
| Status | not is one of | Refunded |Options
| Bullet | Values | Meaning |
|---|---|---|
- Match: | all · any | AND or OR across every filter. Default all. Alias: Combine. |
- Search: | any text | The quick filter — a substring match across every column. Aliases: Quick filter, Find. |
- Only modified rows: | yes · no | Show only rows you have edited in this session. |
- Invert: | yes · no | Show exactly the rows the filters would have hidden. Alias: Inverted. |
Tests
Put not in front of any test to negate it — not contains, not is one of. ! and ¬ work as well.
| Test | Also accepted | Value |
|---|---|---|
| Text | ||
contains | has | a substring |
does not contain | excludes | a substring |
equals | is exactly | the whole value |
does not equal | is not | the whole value |
begins with | starts with | a prefix |
ends with | — | a suffix |
matches regex | regex, matches | a regular expression |
| Numbers | ||
= | ==, equals to, is equal to | a number |
<> | !=, ≠, is not equal to | a number |
< | less than, is less than | a number |
<= | ≤, at most, less than or equal to | a number |
> | greater than, is greater than | a number |
>= | ≥, at least, greater than or equal to | a number |
between | between numbers | 10 and 20 |
| Dates | ||
before | — | a date, e.g. 2026-01-01 |
after | — | a date |
between | — | 2026-01-01 and 2026-03-31 |
in the last days | in the last n days, in the last | a number of days |
| Lists, emptiness, yes/no | ||
is one of | one of, in, any of | a comma-separated list |
is empty | empty, blank | (none) |
is not empty | not empty, filled | (none) |
is | — | true or false |
is true / is false | — | (none) |
A common slip. is is the yes/no test, so | Status | not is | Refunded | does nothing — Refunded is not a yes or a no. For one text value use not is one of or does not equal.
Lists and ranges
A list is comma-separated. An item that itself contains a comma or a quote is wrapped in double quotes, and a quote inside such an item is doubled — the same rule the CSV underneath uses:
| Product | is one of | Atlas Keyboard, "Monitor 27, refurbished", Vector Mouse | A range may be written 10 and 20, 10 to 20, 10 - 20, 10 … 20 or 10, 20.
Saved filters
Named filter sets — the ones in the toolbar's funnel menu. Each gets a ### heading and then exactly the same body as Filters, minus Only modified rows and Invert.
## Saved filters
### Big open deals
- Match: all
| Column | Test | Value |
| ------- | --------- | ----------------- |
| Status | is one of | Invoiced, Pending |
| Revenue | at least | 3000 |
### Refunds to chase
- Match: all
| Column | Test | Value |
| ------ | --------- | -------- |
| Status | is one of | Refunded |The formula language
The same language is used for calculated columns, cell formulas and row-formatting conditions.
Values
- A column — its name in square brackets:
[Unit Price]. - Text — in double quotes:
"Paid". - Numbers —
12,0.075,1e3. - Yes and no —
true,false. - Nothing —
empty,blankornull. - π —
pi.
Operators
Listed loosest-binding first; everything associates to the left except ^.
| Operators | Does |
|---|---|
or | either side is true |
and | both sides are true |
= <> < <= > >= | comparison. !=, ≠, ≤, ≥ and == are accepted too |
& | joins two pieces of text |
+ - | add, subtract |
* / % | multiply, divide, remainder |
^ | raise to a power |
not(…) negates. Arguments may be separated by either a comma or a semicolon.
Functions
| Group | Functions |
|---|---|
| Logic | IF(test, then, else), IFERROR(value, fallback), AND(…), OR(…), NOT(x), COALESCE(…), ISEMPTY(x), ISNUMBER(x), ISERROR(x) |
| Numbers | ABS, ROUND(n, places), ROUNDUP, ROUNDDOWN, FLOOR, CEILING, INT, MOD(a, b), POWER(a, b), SQRT, EXP, LN, LOG10, SIGN, MIN(a, b, …), MAX(a, b, …), CLAMP(n, low, high) |
| Text | LEN, UPPER, LOWER, TRIM, CONCAT(…), LEFT(t, n), RIGHT(t, n), MID(t, from, n), FIND(t, needle), CONTAINS(t, needle), STARTSWITH(t, p), ENDSWITH(t, s), SUBSTITUTE(t, old, new), REPEAT(t, n), MATCHES(t, regex) |
| Conversion | NUMBER(x), TEXT(x, format) |
| Dates | TODAY(), YEAR, MONTH, DAY, DATEDIFF(later, earlier) in whole days |
| Position | ROW() — this row's line number in the CSV |
Three spellings are aliases: VALUE for NUMBER, LOG for LOG10, TRUNC for INT.
Whole-column summaries
Given one column, these read every row in the file — not just the rows a filter leaves on screen — so [Revenue] / SUM([Revenue]) is each row's share of the whole.
| Function | Returns |
|---|---|
SUM([c]) | the total |
AVG([c]) | the mean. AVERAGE and MEAN are synonyms |
MEDIAN([c]) | the median |
MIN([c]) / MAX([c]) | the smallest / largest value |
COUNT([c]) | how many rows |
COUNTA([c]) | how many are not empty |
COUNTBLANK([c]) | how many are empty |
DISTINCT([c]) | how many different values |
STDEV([c]) | the standard deviation |
MIN and MAX are both.MIN([Price]) — one argument, a column — summarizes the whole column. MIN([Price], 10) — two or more arguments — compares values within the row.
When a formula cannot be worked out
Errors are values, not failures: a broken formula shows why in its own cell and leaves every other cell alone.
| Shown | Means |
|---|---|
#SYNTAX? | The formula could not be read — check the brackets and quotes. |
#NAME? | No column by that name. |
#FUNC? | No function by that name. |
#ARGS? | A function got the wrong number of arguments. |
#VALUE? | A value was not a number where one was needed. |
#DIV/0! | Division by zero. |
#CYCLE! | The formula refers back to itself, directly or through another column. |
Examples
[Units] * [Price] * (1 - [Discount])
ROUND([Revenue] / SUM([Revenue]) * 100, 2)
IF([Discount] > 0, ROUND([Revenue] * 0.05, 2), ROUND([Revenue] * 0.07, 2))
UPPER(LEFT([Region], 1)) & LOWER(MID([Region], 2, 99))
IF(DATEDIFF(TODAY(), [Date]) > 30, "overdue", "")
IFERROR(NUMBER([Weight]) / NUMBER([Volume]), "")
[Status] = "Refunded" or [Status] = "Pending"
MATCHES([Order], "^SO-1[0-9]{4}$")Notes and edge cases
- Plaintable rewrites the file. Whenever the view changes in the app, the whole file is written out in its canonical shape: padded table pipes, sections in a fixed order, backticks around formulas. Your content survives; your spacing may not.
- Your own sections survive. Any
##heading Plaintable does not recognise is preserved verbatim and written back at the end of the file. It is the place to leave a note about what the view is for. - Nothing here changes the CSV. Hiding a column does not delete it, hiding a row does not remove it, and a calculated column is never saved. Export the current view if you want the calculated values in a file.
- Bad input is ignored, not fatal. An unreadable format falls back to automatic, an unknown alignment to the default, a filter row that makes no sense is dropped. A typo can never hide your data.
- Editing it while the file is open works both ways. Plaintable watches the view file with a file presenter and a plain filesystem watch, so changes from
sed,git checkoutor vim are picked up just as well as changes from a coordinating editor. - Line numbers are CSV line numbers. The header is line 1, so the first data row is line 2 — the same numbering the gutter shows, and it does not move when you sort or filter.