+
{% for child in node.children %}
- {{ Node(child.type, child, page) }}
+ {{ Node(child.type, child, ctx) }}
{% endfor %}
diff --git a/docs/src/_includes/nodes/TableOfContents.njk b/docs/src/_includes/nodes/TableOfContents.njk
new file mode 100644
index 0000000..ae8a723
--- /dev/null
+++ b/docs/src/_includes/nodes/TableOfContents.njk
@@ -0,0 +1,19 @@
+{% macro renderHeadings(headings) %}
+
+ {% for heading in headings %}
+ -
+ {{ heading.text }}
+ {% if heading.children %}
+ {{ renderHeadings(heading.children)}}
+ {% endif %}
+
+ {% endfor %}
+
+{% endmacro %}
+
+{% macro render(node, ctx) %}
+
Table of Contents
+
+ {{ renderHeadings(ctx.headings) }}
+
+{% endmacro %}
diff --git a/docs/src/about.tce b/docs/src/about.tce
index 47733ba..b823c14 100644
--- a/docs/src/about.tce
+++ b/docs/src/about.tce
@@ -5,229 +5,250 @@ description
Terrace gets out of your way to let you just write
Section dark
- class pt-40 md:pt-32 max-w-prose
+ class flex flex-col md:flex-row gap-16
- Heading 1 About Terrace
+ Block
+ class w-full lg:w-1/3
- Markdown
- Terrace is a tiny system for storing data in human-friendly text files.
+ TableOfContents
- It allows you to mix all sorts of text and data formats together in an intuitive way without getting caught up in syntax pomp and ceremony.
+ Block
+ class max-w-prose
- CodeBlock terrace
- title An Essay on Rockets
- date 2022-02-22
- by John Doe
+ Heading 1 About Terrace
- heading The ideal rocket equation
+ Markdown
+ Terrace is a tiny system for storing data in human-friendly text files.
- equation LaTeX
- \Delta v=v_{\text{e}}\ln {\frac {m_{0}}{m_{f}}}=I_{\text{sp}}g_{0}\ln {\frac {m_{0}}{m_{f}}}
+ It allows you to mix all sorts of text and data formats together in an intuitive way without getting caught up in syntax pomp and ceremony.
- markdown
- A fundamental equation in astrophysics, the *ideal rocket equation* describes the total potential velocity of an idealized vehicle propelled by expelling some of its own mass.
+ CodeBlock terrace
+ title An Essay on Rockets
+ date 2022-02-22
+ by John Doe
- ...
+ heading The ideal rocket equation
- Markdown
- class prose-invert
- You can use Terrace to write documents, web pages, configuration files, data storage, or whatever else you come up with!
+ equation LaTeX
+ \Delta v=v_{\text{e}}\ln {\frac {m_{0}}{m_{f}}}=I_{\text{sp}}g_{0}\ln {\frac {m_{0}}{m_{f}}}
- [This page](https://git.thederf.com/thederf/Terrace/src/branch/main/docs/src/about.tce) is written using Terrace!
+ markdown
+ A fundamental equation in astrophysics, the *ideal rocket equation* describes the total potential velocity of an idealized vehicle propelled by expelling some of its own mass.
+
+ ...
+
+ Markdown
+ class prose-invert
+ You can use Terrace to write documents, web pages, configuration files, data storage, or whatever else you come up with!
+
+ [This page](https://git.thederf.com/thederf/Terrace/src/branch/main/docs/src/about.tce) is written using Terrace!
Section light
- class max-w-prose
- Heading 2 Background
+ class flex flex-col md:flex-row gap-16
- Markdown
- Terrace was originally envisioned out of frustration at the massive usability gulf for embedding custom blocks in simple, human-friendly markup formats such as [Markdown](), and capable but complex block-based systems such as [Project Gutenberg](https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/writing-your-first-block-type/) and [Editor.js](https://editorjs.io/saving-data/).
+ Block
+ class w-1/3
- Heading 3 The Complexity Problem
- class mt-8 mb-4
+ Block
+ class max-w-prose
- Markdown
- **A simple markdown document:**
+ Heading 2 Background
- CodeBlock markdown
- # My Page Title
+ Markdown
+ Terrace was originally envisioned out of frustration at the massive usability gulf for embedding custom blocks in simple, human-friendly markup formats such as [Markdown](), and capable but complex block-based systems such as [Project Gutenberg](https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/writing-your-first-block-type/) and [Editor.js](https://editorjs.io/saving-data/).
- Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad.
+ Heading 3 The Complexity Problem
+ class mt-8 mb-4
- 1. Magna id sint consequat quis esse tempor.
- 2. Veniam eu id esse occaecat eu.
- 3. Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua.
+ Markdown
+ **A simple markdown document:**
- [Put my-custom-component here somehow]
+ CodeBlock markdown
+ # My Page Title
- Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim.
-
- Markdown
- **That same document in a *simplified* Editor.js schema:**
-
- CodeBlock javascript
- class max-h-[500px] overflow-y-auto
- [
- {
- type: "header",
- data: {
- level: 1,
- text: "My Page Title"
- }
- },
- {
- type: "paragraph",
- data: {
- text: "Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad."
- }
- },
- {
- type: "list",
- data: {
- style: "ordered",
- items: [
- "Magna id sint consequat quis esse tempor.",
- "Veniam eu id esse occaecat eu.",
- "Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua."
- ]
- }
- },
- {
- type: "my-custom-component",
- data: {
- option1: "value",
- option2: "value",
- text: "Text"
- }
- },
- {
- type: "paragraph",
- data: {
- text: "Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim."
- }
- }
- ]
-
- Heading 3 MDX & MDC
- class mt-16 mb-4
-
- Markdown
- Other attempts, such as [MDX](https://mdxjs.com/) and [MDC](https://content.nuxtjs.org/guide/writing/mdc/) have been made to solve this problem, allowing you to incorporate complex content *inside* markup documents.
-
- However, they suffer from a number of problems:
-
- 1. Tightly coupled to the JavaScript ecosystem
- 2. Lock you out of other markup formats, such as [AsciiDoc](https://asciidoc.org/)
- 3. Difficult to parse - Markdown was not intended as a general-purpose syntax
- 4. Data can't be extracted from markup without rendering the entire document to HTML
- 5. Ugly. Just look at this:
-
- CodeBlock markdown
- # My Page Title
-
- Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad.
-
- 1. Magna id sint consequat quis esse tempor.
- 2. Veniam eu id esse occaecat eu.
- 3. Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua.
-
- [MDX Example]
-
- Text
-
-
- [MDC Example]
- ::MyCustomComponent{option1="value", option2="value"}
- Text
- ::
-
-
- Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim.
-
- Heading 3 Terrace Prototype
- class mt-16 mb-4
-
- Markdown
- I first attempted to solve this problem using an [S-Expression](https://en.wikipedia.org/wiki/S-expression)-based syntax for defining blocks.
-
- CodeBlock lisp
- (heading
- (level 1)
- My Page Title
- )
-
- (markdown
- Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad.
-
- 1. Magna id sint consequat quis esse tempor.
- 2. Veniam eu id esse occaecat eu.
- 3. Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua.
- )
-
- (my-custom-component
- (option1 value)
- (option2 value)
- Text
- )
-
- (markdown
- Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim.
- )
-
- Markdown
- While quite flexible and clean-looking, problems became apparent with handling whitespace and escaping parenthesis in text.
- The parser quickly became much more extensive than initially envisioned.
-
- Nevertheless, it proved the concept that cleanly combining functional blocks and markup text was quite reasonable.
-
- Not long after, I stumbled upon [Tree Notation](https://treenotation.org) by [Brek Yunits](https://www.breckyunits.com/). Playing around with it, the potential of just *getting rid of the parenthesis* was quite apparent. Simpler syntax for humans to write, and far less work required for parsing & escaping since the only control characters are newlines and spaces:
-
- CodeBlock terrace
- heading My Page Title
- level 1
-
- markdown
Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad.
1. Magna id sint consequat quis esse tempor.
2. Veniam eu id esse occaecat eu.
3. Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua.
- my-custom-block
- option1 value
- option2 value
- Text
+ [Put my-custom-component here somehow]
- markdown
Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim.
- Markdown
- Ultimately though, using Tree Notation still proved too messy. After working with the grammar system and spreadsheet-style data model for a good while, my gut said all this use case needed was a simple line-based parser.
+ Markdown
+ **That same document in a *simplified* Editor.js schema:**
- I started Terrace to implement a similar syntax, but with the following goals:
+ CodeBlock javascript
+ class max-h-[500px] overflow-y-auto
+ [
+ {
+ type: "header",
+ data: {
+ level: 1,
+ text: "My Page Title"
+ }
+ },
+ {
+ type: "paragraph",
+ data: {
+ text: "Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad."
+ }
+ },
+ {
+ type: "list",
+ data: {
+ style: "ordered",
+ items: [
+ "Magna id sint consequat quis esse tempor.",
+ "Veniam eu id esse occaecat eu.",
+ "Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua."
+ ]
+ }
+ },
+ {
+ type: "my-custom-component",
+ data: {
+ option1: "value",
+ option2: "value",
+ text: "Text"
+ }
+ },
+ {
+ type: "paragraph",
+ data: {
+ text: "Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim."
+ }
+ }
+ ]
- 1. Make as few decisions as possible for the user
- 2. Be as fast, tiny, and light on resources as possible
+ Heading 3 MDX & MDC
+ class mt-16 mb-4
- Six or seven parser rewrites later, it finally works mostly how I envisioned it. :)
+ Markdown
+ Other attempts, such as [MDX](https://mdxjs.com/) and [MDC](https://content.nuxtjs.org/guide/writing/mdc/) have been made to solve this problem, allowing you to incorporate complex content *inside* markup documents.
+
+ However, they suffer from a number of problems:
+
+ 1. Tightly coupled to the JavaScript ecosystem
+ 2. Lock you out of other markup formats, such as [AsciiDoc](https://asciidoc.org/)
+ 3. Difficult to parse - Markdown was not intended as a general-purpose syntax
+ 4. Data can't be extracted from markup without rendering the entire document to HTML
+ 5. Ugly. Just look at this:
+
+ CodeBlock markdown
+ # My Page Title
+
+ Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad.
+
+ 1. Magna id sint consequat quis esse tempor.
+ 2. Veniam eu id esse occaecat eu.
+ 3. Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua.
+
+ [MDX Example]
+
+ Text
+
+
+ [MDC Example]
+ ::MyCustomComponent{option1="value", option2="value"}
+ Text
+ ::
+
+
+ Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim.
+
+ Heading 3 Terrace Prototype
+ class mt-16 mb-4
+
+ Markdown
+ I first attempted to solve this problem using an [S-Expression](https://en.wikipedia.org/wiki/S-expression)-based syntax for defining blocks.
+
+ CodeBlock lisp
+ (heading
+ (level 1)
+ My Page Title
+ )
+
+ (markdown
+ Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad.
+
+ 1. Magna id sint consequat quis esse tempor.
+ 2. Veniam eu id esse occaecat eu.
+ 3. Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua.
+ )
+
+ (my-custom-component
+ (option1 value)
+ (option2 value)
+ Text
+ )
+
+ (markdown
+ Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim.
+ )
+
+ Markdown
+ While quite flexible and clean-looking, problems became apparent with handling whitespace and escaping parenthesis in text.
+ The parser quickly became much more extensive than initially envisioned.
+
+ Nevertheless, it proved the concept that cleanly combining functional blocks and markup text was quite reasonable.
+
+ Not long after, I stumbled upon [Tree Notation](https://treenotation.org) by [Brek Yunits](https://www.breckyunits.com/). Playing around with it, the potential of just *getting rid of the parenthesis* was quite apparent. Simpler syntax for humans to write, and far less work required for parsing & escaping since the only control characters are newlines and spaces:
+
+ CodeBlock terrace
+ heading My Page Title
+ level 1
+
+ markdown
+ Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad.
+
+ 1. Magna id sint consequat quis esse tempor.
+ 2. Veniam eu id esse occaecat eu.
+ 3. Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua.
+
+ my-custom-block
+ option1 value
+ option2 value
+ Text
+
+ markdown
+ Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim.
+
+ Markdown
+ Ultimately though, using Tree Notation still proved too messy. After working with the grammar system and spreadsheet-style data model for a good while, my gut said all this use case needed was a simple line-based parser.
+
+ I started Terrace to implement a similar syntax, but with the following goals:
+
+ 1. Make as few decisions as possible for the user
+ 2. Be as fast, tiny, and light on resources as possible
+
+ Six or seven parser rewrites later, it finally works mostly how I envisioned it. :)
Section dark
- class max-w-prose
+ class flex flex-col md:flex-row gap-16
- Heading 2 Development Goals
+ Block
+ class w-1/3
- Markdown
- class prose-invert
- Terrace development is guided by the following goals.
+ Block
+ class max-w-prose
- - **Tiny, primitive core - no “framework”**
- - Use the most boring patterns possible for implementing the core library.
- - Avoid dependencies
- - Easily port Terrace in any language or environment
- - **Avoid dynamic allocation**
- - Since we can work directly off of string slices without the need for ASTs and escaping systems, might as well avoid dynamic allocations altogether!
- - **Minimize assumptions**
- - Let people use Terrace as a building block for all sorts of languages
+ Heading 2 Development Goals
- Make note of this before submitting change requests. If your changes don't fit these goals, they may be denied. In which case, you could try building a separate library on top of the Terrace cor.
+ Markdown
+ class prose-invert
+ Terrace development is guided by the following goals.
- Footer
+ - **Tiny, primitive core - no “framework”**
+ - Use the most boring patterns possible for implementing the core library.
+ - Avoid dependencies
+ - Easily port Terrace in any language or environment
+ - **Avoid dynamic allocation**
+ - Since we can work directly off of string slices without the need for ASTs and escaping systems, might as well avoid dynamic allocations altogether!
+ - **Minimize assumptions**
+ - Let people use Terrace as a building block for all sorts of languages
+
+ Make note of this before submitting change requests. If your changes don't fit these goals, they may be denied. In which case, you could try building a separate library on top of the Terrace cor.
+
+ Footer
diff --git a/docs/src/index.tce b/docs/src/index.tce
index f31c651..5a10764 100644
--- a/docs/src/index.tce
+++ b/docs/src/index.tce
@@ -5,10 +5,12 @@ description
Terrace gets out of your way to let you just write
Section light
- class pt-40 md:pt-36 pb-48 md:pb-64 flex flex-col gap-16 md:flex-row md:gap-48
+ class pt-24 flex flex-col gap-16 md:flex-row lg:gap-48
Block
class flex flex-col gap-8 w-full items-start
Logo light
+ class hidden lg:flex
+
Markdown
class prose-ul:list-none
A simple structured data syntax for
@@ -74,7 +76,7 @@ Section dark
Heading 2 Uses
Block
- class flex flex-col space-between gap-16 md:flex-row md:gap-48
+ class flex flex-col space-between gap-16 md:flex-row lg:gap-48
Block
class max-w-prose
@@ -122,7 +124,7 @@ Section light
Heading 2 Core
Block
- class flex flex-col space-between gap-16 md:flex-row md:gap-48
+ class flex flex-col space-between gap-16 md:flex-row lg:gap-48
Block
Heading 3 Tiny -
Really Tiny
@@ -165,7 +167,7 @@ Section dark
Heading 2 Learn More
Block
- class flex flex-col space-between gap-16 md:flex-row md:gap-48
+ class flex flex-col space-between gap-16 md:flex-row lg:gap-48
Block
class max-w-prose
diff --git a/docs/src/parser/nodes/Heading.js b/docs/src/parser/nodes/Heading.js
index 342da08..6fb5310 100644
--- a/docs/src/parser/nodes/Heading.js
+++ b/docs/src/parser/nodes/Heading.js
@@ -4,7 +4,7 @@ module.exports = async function (doc, rootLevel, pageData) {
const { next, line, match, tail, level, head } = doc
- const headingLevel = tail().split(' ')[0]
+ const headingLevel = +tail().split(' ')[0]
const text = tail().split(' ').slice(1).join(' ')
const slug = slugify(text)
@@ -13,7 +13,8 @@ module.exports = async function (doc, rootLevel, pageData) {
level: headingLevel,
text,
slug,
- class: ''
+ class: '',
+ children: []
}
while (await next(rootLevel)) {
diff --git a/docs/src/parser/nodes/Logo.js b/docs/src/parser/nodes/Logo.js
new file mode 100644
index 0000000..c7e0941
--- /dev/null
+++ b/docs/src/parser/nodes/Logo.js
@@ -0,0 +1,23 @@
+const { contentAsText } = require('../helpers.js')
+
+module.exports = async function (doc, rootLevel) {
+ const { next, line, match, tail, level, head } = doc
+
+ const node = {
+ type: head(),
+ variant: tail() || 'neutral',
+ class: '',
+ href: '',
+ text: ''
+ }
+
+ while (await next(rootLevel)) {
+ if (match('class')) node.class = tail()
+ else if (match('href')) node.href = tail()
+ else {
+ node.text = await contentAsText(doc, rootLevel, true)
+ }
+ }
+
+ return node
+}
diff --git a/docs/src/parser/nodes/TableOfContents.js b/docs/src/parser/nodes/TableOfContents.js
new file mode 100644
index 0000000..598b3e9
--- /dev/null
+++ b/docs/src/parser/nodes/TableOfContents.js
@@ -0,0 +1,14 @@
+module.exports = async function (doc, rootLevel) {
+ const { next, head, tail, match } = doc
+
+ const node = {
+ type: head(),
+ class: '',
+ }
+
+ while (await next(rootLevel)) {
+ if (match('class')) node.class = tail()
+ }
+
+ return node
+}
diff --git a/docs/src/parser/nodes/index.js b/docs/src/parser/nodes/index.js
index c25e010..a3021bd 100644
--- a/docs/src/parser/nodes/index.js
+++ b/docs/src/parser/nodes/index.js
@@ -5,6 +5,7 @@ module.exports.Section = async (doc, rootLevel, ...args) => {
const variant = doc.tail()
return { variant, ...(await parseNode(doc, rootLevel, ...args)) }
}
+module.exports.TableOfContents = require('./TableOfContents.js')
module.exports.Heading = require('./Heading.js')
module.exports.Button = require('./Button.js')
module.exports.Icon = require('./Icon.js')
@@ -12,9 +13,6 @@ module.exports.Icon = require('./Icon.js')
module.exports.Markdown = require('./Markdown.js')
module.exports.CodeBlock = require('./CodeBlock.js')
module.exports.CodeExample = require('./CodeExample.js')
-module.exports.Logo = doc => ({
- type: `Logo`,
- variant: doc.tail() || 'light'
-})
+module.exports.Logo = require('./Logo.js')
module.exports.Footer = require('./Footer.js')
diff --git a/docs/src/parser/page.js b/docs/src/parser/page.js
index 8789025..22d92e8 100644
--- a/docs/src/parser/page.js
+++ b/docs/src/parser/page.js
@@ -27,7 +27,20 @@ module.exports = async function(doc) {
}
}
- console.dir(pageData, { depth: null })
+ // Structure headings into tree.
+ pageData.headings.forEach((heading, index) => {
+ let parent = null
+ for (let i = index; i > 0; --i) {
+ if (pageData.headings[i].level === heading.level - 1) {
+ parent = pageData.headings[i]
+ }
+ }
+
+ if (parent) parent.children.push(heading)
+ })
+
+ pageData.headings = pageData.headings.filter(h => h.level === 2)
+ console.dir(pageData.headings, { depth: null })
return pageData
}