DEV Community

Cover image for PTTJS — A Text‑Based Format for Complex Tables
Konstantin Kolomeitsev
Konstantin Kolomeitsev

Posted on

PTTJS — A Text‑Based Format for Complex Tables

PTTJS (Plain Text Table JavaScript) is a table format I created to solve my own pain points—and, judging by the feedback I’ve already received, I’m not the only one who has felt the limitations of existing text‑table formats.

A JavaScript library with a parser + serializer and an Obsidian plugin are already available.

Goal

The main goal of PTTJS is to provide a text format that can faithfully store complex tables—far beyond what CSV or Markdown can handle—while still remaining human‑readable.

Motivation

I tolerated the shortcomings of other formats for a long time, but eventually the trade‑offs became impossible to ignore. Key drivers were:

  1. Feeding richer tables to LLMs. Documents often contain intricate, merged‑cell tables that can’t be flattened to CSV/Markdown, forcing us to reach for heavier multimodal models.
  2. Extracting tables via CV into plain text, not into a deeply nested JSON blob that’s unreadable until you re‑render it.
  3. Letting an LLM manipulate an entire table natively—rows, columns, formulas—without shoe‑horning everything through Google Sheets or Excel.
  4. Opening tables without special software. Most tables end up trapped in XLS(X) or ODT; PTTJS keeps them plain text.
  5. Editing complex tables in plain‑text tools like Obsidian.
  6. Personal dislike of Google Sheets/Excel (surely I’m not the only one!).

the problem is indicated in points 1 and 2, the format is recognized normally and inserted into the text

the problem is indicated in points 1 and 2, the format is recognized normally and inserted into the text

Format Overview

The very first line is always an annotation:

|PTTJS 1.0|encoding=UTF‑8|
Enter fullscreen mode Exit fullscreen mode

Pages

A table can span multiple pages:

|(@P1|Page Name){
    …table data…
}|
Enter fullscreen mode Exit fullscreen mode

Page markers are optional; omit them and the whole table lives on a single page.

Cells

Every cell starts with | and ends with >:

|H([1|1]1|1|@C1)>
Enter fullscreen mode Exit fullscreen mode
  • H — optional marks a header cell
  • ([X|Y]) — zero‑based cell index (optional; auto‑filled if absent)
  • (X|Y) — cell span (defaults to 1|1; always declared in the top‑left merged cell)
  • (@ID) — cell id for references

Row ends with:

<|
Enter fullscreen mode Exit fullscreen mode

If a row is otherwise empty but you still have rows below it, include at least one empty cell: |><|.

Forbidden characters inside cell content (\n | > < { }) must be URL‑encoded:

\n  → %5Cn
|   → %7C
>   → %3E
<   → %3C
{   → %7B
}   → %7D
Enter fullscreen mode Exit fullscreen mode

The library provides escapeValue / unescapeValue helpers.

Scripts

>>>SCRIPT
…typings, formulas, styling…
<<<SCRIPT
Enter fullscreen mode Exit fullscreen mode
  • Always placed after all table data.
  • Can work across pages.
  • Use <= for CSS‑like styles.
  • Use => for cell typings.
  • Use = for formulas.
  • Target a range with :—e.g. 0:0|0 (whole first row) or 0|0:0 (whole first column).

The cells always contain up-to-date information. When we add a script, it recalculates the table data and adds the up-to-date information to the cells.

Examples

1 – Basic Table

|PTTJS 1.0|
|H>Plate Number|H>Year|H>Make & Model<|
|>080XXX02|>2005|>LEXUS RX 350<|
|>787XXX16|>2015|>GEELY GC7<|
|>871XXX05|>1997|>TOYOTA IPSUM<|
|>A602XXX|>1996|>MITSUBISHI PAJERO<|
|>890XXX02|>1997|>TOYOTA LAND CRUISER PRADO<|
|>216XXX13|>2007|>DAEWOO NEXIA<|
Enter fullscreen mode Exit fullscreen mode

2 – With Explicit Indexes

|PTTJS 1.0|encoding=UTF‑8|
|H([0|0])>Plate Number|H([1|0])>Year|H([2|0])>Make & Model<|
|([0|1])>080XXX02|([1|1])>2007|([2|1])>LEXUS RX 350<|
…
Enter fullscreen mode Exit fullscreen mode

3 – Complex Header (Merged Cells)

|PTTJS 1.0|encoding=UTF‑8|
|H(1|2)>Plate Number|H(2|1)>Vehicle Data|H><|
|H>|H>Year|H>Make & Model<|
|>080XXX02|>2007|>LEXUS RX 350<|
…
Enter fullscreen mode Exit fullscreen mode

visualization from Obsidian

visualization from Obsidian

4 – With Scripts

|PTTJS 1.0|encoding=UTF‑8|
|H([0|0])>Plate Number|H([1|0])>Year|H([2|0])>Make & Model<|
|([0|1])>080XXX02|([1|1])>2007|([2|1])>LEXUS RX 350<|
…
|([0|7])><|
|([0|8])>Average Year|([1|8])>2003<|

>>>SCRIPT
(1|1,1|6)=>NUMBER(2,' ')
(1|8)=DIV(SUM(1|1,1|6),COUNT(1|1,1|6))
(0|8:8)<=BORDER(each,2,solid,#000)
<<<SCRIPT
Enter fullscreen mode Exit fullscreen mode

Row 9 calculates the average year; numeric formatting is applied; a black 2 px border is added to the "Average Year" label cell.

Roadmap

  1. Execute script functions directly on the parsed Store object.
  2. Import/export converters (XLS(X), ODT, CSV, MD ↔ PTTJS).
  3. Libraries in other languages.
  4. Lightweight web UI for editing PTTJS.

Contributing

The GitHub repo is public—PRs and ideas are very welcome!

Final Words

Thanks for reading! I hope PTTJS helps you tackle the same table woes I faced. Feel free to reach out, fork the repo, or open an issue—let’s make complex tables easy together.

Top comments (0)