@entropicwarrior/sdoc 0.1.17 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/reference/sdoc-authoring.sdoc +110 -6
- package/lexica/specification.sdoc +185 -27
- package/package.json +1 -1
- package/src/notion-renderer.js +3 -0
- package/src/sdoc.js +213 -129
- package/src/slide-renderer.js +21 -6
- package/docs/guide/intro.sdoc +0 -112
- package/docs/guide/notion-sync.sdoc +0 -177
- package/docs/guide/setup.sdoc +0 -112
- package/docs/guide/why-sdoc.sdoc +0 -206
- package/docs/index.sdoc +0 -39
- package/docs/reference/api.sdoc +0 -208
- package/docs/reference/cli.sdoc +0 -188
- package/docs/reference/syntax.sdoc +0 -502
- package/docs/tutorials/first-steps.sdoc +0 -117
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
{
|
|
5
5
|
type: skill
|
|
6
6
|
|
|
7
|
-
sdoc-version: 0.
|
|
7
|
+
sdoc-version: 0.2
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
# About @about
|
|
@@ -178,7 +178,7 @@ Content of Section B.
|
|
|
178
178
|
|
|
179
179
|
**Important:** \`{[.]}\` with the closing brace on the same line creates an empty list — the brace closes the block immediately. Always put the closing \`}\` on a separate line after the items.
|
|
180
180
|
|
|
181
|
-
Implicit lists
|
|
181
|
+
Implicit lists work for both bullet (\`-\`) and numbered (\`1.\`, \`2.\`, etc.) items. Each item must be a single line. For multi-line item titles or item body content, use the explicit \`{[.]}\` or \`{[#]\` block form.
|
|
182
182
|
|
|
183
183
|
**Task lists** — checkbox syntax inside explicit list blocks:
|
|
184
184
|
|
|
@@ -244,6 +244,27 @@ Content of Section B.
|
|
|
244
244
|
```
|
|
245
245
|
|
|
246
246
|
\`borderless\` removes borders and row striping. \`headerless\` treats all rows as data (no header). Flags combine in any order.
|
|
247
|
+
|
|
248
|
+
Width and alignment flags control table sizing and position:
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
{[table 60% center]
|
|
252
|
+
Endpoint | Status
|
|
253
|
+
/v2/weather | Active
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
{[table auto]
|
|
257
|
+
Key | Value
|
|
258
|
+
Version | 2.0
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
{[table 400px right borderless]
|
|
262
|
+
v2.0 | Current
|
|
263
|
+
v1.0 | Deprecated
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Width: \`auto\` (shrink to content), \`NN%\` (percentage), or \`NNpx\` (pixels). Default is 100%. Alignment: \`left\` (default), \`center\`, or \`right\`. All flags compose freely in any order.
|
|
247
268
|
}
|
|
248
269
|
|
|
249
270
|
# Inline Formatting @inline-formatting
|
|
@@ -279,7 +300,7 @@ Content of Section B.
|
|
|
279
300
|
 
|
|
280
301
|
```
|
|
281
302
|
|
|
282
|
-
Autolinks: \`\<https://example.com\>\`
|
|
303
|
+
Autolinks: \`\<https://example.com\>\` — angle brackets are optional; bare URLs starting with \`http://\`, \`https://\`, or \`mailto:\` are also auto-linked.
|
|
283
304
|
|
|
284
305
|
Math: Use \`\$...\$\` for inline math and \`\$\$...\$\$\` for display math. Use \`\\\`\\\`\\\`math\` code fences for multi-line equations. A plain \`\$\` followed by a digit (e.g. \`\$100\`) does not trigger math mode.
|
|
285
306
|
}
|
|
@@ -380,7 +401,7 @@ Content of Section B.
|
|
|
380
401
|
@meta {
|
|
381
402
|
type: doc
|
|
382
403
|
|
|
383
|
-
sdoc-version: 0.
|
|
404
|
+
sdoc-version: 0.2
|
|
384
405
|
|
|
385
406
|
company: Irreversible Inc.
|
|
386
407
|
|
|
@@ -420,7 +441,7 @@ Content of Section B.
|
|
|
420
441
|
|
|
421
442
|
\`tags: tag1, tag2\` — comma-separated tags.
|
|
422
443
|
|
|
423
|
-
\`sdoc-version: 0.
|
|
444
|
+
\`sdoc-version: 0.2\` — SDOC format version. A parser warning is
|
|
424
445
|
emitted when this key is missing from \`@meta\`.
|
|
425
446
|
}
|
|
426
447
|
|
|
@@ -441,7 +462,7 @@ Content of Section B.
|
|
|
441
462
|
{
|
|
442
463
|
Backslash escapes special characters: \`\\\\\` \`\\{\` \`\\}\` \`\\@\`
|
|
443
464
|
\`\\[\` \`\\]\` \`\\(\` \`\\)\` \`\\*\` \`\\~\` \`\\#\` \`\\!\` \`\\\<\`
|
|
444
|
-
\`\\\>\` \`\\\$\` \`\\+\` \`\\=\` \`\\-\` \`\\^\`
|
|
465
|
+
\`\\\>\` \`\\\$\` \`\\+\` \`\\=\` \`\\-\` \`\\^\` \`\\?\`
|
|
445
466
|
|
|
446
467
|
A line starting with \`\\#\` renders as a literal \`#\` (not a heading). Use \`\\\$\` to prevent a dollar sign from starting math mode.
|
|
447
468
|
}
|
|
@@ -454,6 +475,89 @@ Content of Section B.
|
|
|
454
475
|
- **Commas:** commas between list items or scopes are allowed but ignored — use them if you find them readable.
|
|
455
476
|
}
|
|
456
477
|
}
|
|
478
|
+
|
|
479
|
+
# Scope Types @scope-types
|
|
480
|
+
{
|
|
481
|
+
A \`:type\` annotation on a heading gives the scope semantic meaning. Place it after the optional \`@id\`:
|
|
482
|
+
|
|
483
|
+
```
|
|
484
|
+
# User Authentication @auth :requirement
|
|
485
|
+
{
|
|
486
|
+
The system shall authenticate users via OAuth 2.0.
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
# OAuth Flow :specification @oauth-flow
|
|
490
|
+
{
|
|
491
|
+
Implements @auth using the authorization code flow.
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
# Deprecation Notice :warning
|
|
495
|
+
{
|
|
496
|
+
This API will be removed in v3.0.
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
{[.]
|
|
501
|
+
- Syntax: \`# Title @id :type\` or \`# Title :type @id\` or \`# Title :type\` — both orderings work
|
|
502
|
+
- The colon requires whitespace before it: \`# Note: Important\` is NOT a type — the colon is part of the title
|
|
503
|
+
- Any string is valid. Well-known types: \`schema\`, \`example\`, \`requirement\`, \`specification\`, \`definition\`, \`note\`, \`warning\`, \`test\`, \`task\`, \`api\`, \`config\`, \`deprecated\`, \`comment\`
|
|
504
|
+
- Works with K&R style: \`# Title :warning {\`
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
The special type \`:comment\` makes a scope that is in the AST but not rendered — useful for editorial notes and AI agent instructions:
|
|
508
|
+
|
|
509
|
+
```
|
|
510
|
+
# TODO :comment
|
|
511
|
+
{
|
|
512
|
+
Rewrite this section after the API stabilises.
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
# Data Blocks @data-blocks
|
|
518
|
+
{
|
|
519
|
+
Add \`:data\` to a JSON code fence to have the parser parse the content into structured data on the AST node:
|
|
520
|
+
|
|
521
|
+
`````
|
|
522
|
+
```json :data
|
|
523
|
+
{
|
|
524
|
+
"name": "SDOC",
|
|
525
|
+
"version": "0.2",
|
|
526
|
+
"features": ["scopes", "lists", "tables"]
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
`````
|
|
530
|
+
|
|
531
|
+
{[.]
|
|
532
|
+
- Syntax: \` \`\`\`json :data \` — the \`:data\` flag follows the language tag
|
|
533
|
+
- Invalid JSON produces a parse error
|
|
534
|
+
- JSON-only in v0.2
|
|
535
|
+
- Without \`:data\`, JSON code blocks remain raw text (existing behaviour)
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
# Comments @comments
|
|
540
|
+
{
|
|
541
|
+
**Line comments** — \`//\` at the start of a line (after optional indentation) skips the line entirely. Not in the AST, not rendered:
|
|
542
|
+
|
|
543
|
+
```
|
|
544
|
+
# Config @config
|
|
545
|
+
{
|
|
546
|
+
// TODO: add validation
|
|
547
|
+
The config file uses JSON format.
|
|
548
|
+
|
|
549
|
+
// hidden from output
|
|
550
|
+
See @setup for details.
|
|
551
|
+
}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
{[.]
|
|
555
|
+
- Mid-line \`//\` has no effect — URLs like \`https://example.com\` are safe
|
|
556
|
+
- Inside code blocks: \`//\` has no special meaning
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
**Comment scopes** — use the \`:comment\` scope type for structured non-rendered content (see @scope-types above).
|
|
560
|
+
}
|
|
457
561
|
}
|
|
458
562
|
|
|
459
563
|
# Common Mistakes @common-mistakes
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
# SDOC Specification v0.
|
|
1
|
+
# SDOC Specification v0.2 @sdoc-spec
|
|
2
2
|
{
|
|
3
3
|
# Meta @meta
|
|
4
4
|
{
|
|
5
5
|
type: doc
|
|
6
6
|
|
|
7
|
-
sdoc-version: 0.
|
|
7
|
+
sdoc-version: 0.2
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
# About @about
|
|
11
11
|
{
|
|
12
|
-
The formal SDOC v0.
|
|
13
|
-
lists, tables, code blocks, inline formatting, references,
|
|
14
|
-
the meta scope
|
|
15
|
-
|
|
16
|
-
user-facing
|
|
12
|
+
The formal SDOC v0.2 specification. Defines syntax for scopes,
|
|
13
|
+
lists, tables, code blocks, inline formatting, references,
|
|
14
|
+
the meta scope, scope types, data blocks, and comments.
|
|
15
|
+
Includes the formal EBNF grammar. Read for edge cases and
|
|
16
|
+
parser behaviour questions. For a friendlier user-facing
|
|
17
|
+
reference, see \`docs/reference/syntax.sdoc\`.
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
# Overview @overview
|
|
@@ -51,12 +52,17 @@
|
|
|
51
52
|
{
|
|
52
53
|
```
|
|
53
54
|
# Title text @id
|
|
55
|
+
# Title text @id :type
|
|
56
|
+
# Title text :type @id
|
|
57
|
+
# Title text :type
|
|
54
58
|
```
|
|
55
59
|
|
|
56
60
|
{[.]
|
|
57
61
|
- The line must start with `#` (after optional indentation)
|
|
58
62
|
- Multiple `#` characters are allowed but do not affect depth. Depth comes only from scope nesting
|
|
59
|
-
- The optional `@id` must appear at the end of the line, separated by whitespace
|
|
63
|
+
- The optional `@id` must appear at the end of the line (or before `:type`), separated by whitespace
|
|
64
|
+
- The optional `:type` assigns a scope type (see @scope-types). Both `@id :type` and `:type @id` orderings are valid
|
|
65
|
+
- `:type` requires whitespace before the colon, so `# Note: Important` is NOT a scope type — the colon is part of the title
|
|
60
66
|
- If no `@id` is present, the scope has no ID
|
|
61
67
|
- If you need a literal `@` in the title, escape it (`\@`)
|
|
62
68
|
}
|
|
@@ -122,7 +128,7 @@ Content of Section B.
|
|
|
122
128
|
- The opener must be the last token on the line (trailing whitespace is allowed)
|
|
123
129
|
- Applies to `{`, `{[.]`, `{[#]`, `{[table]`, and `{[table <flags>]`
|
|
124
130
|
- Also works on list-item shorthand lines (e.g., `- Item {`)
|
|
125
|
-
- Table options (e.g., `{[table borderless]`) work in K&R style: `# Data {[table borderless]`
|
|
131
|
+
- Table options (e.g., `{[table borderless]`, `{[table 60% center]`) work in K&R style: `# Data {[table borderless]`
|
|
126
132
|
- Escaped braces (`\{`) are not treated as openers
|
|
127
133
|
- The closing `}` must still appear on its own line
|
|
128
134
|
- Inline blocks (`{ content }`) are not affected; a line ending with `}` is not treated as K&R
|
|
@@ -344,9 +350,33 @@ Content of Section B.
|
|
|
344
350
|
{[.]
|
|
345
351
|
- `borderless` removes all table borders and row striping
|
|
346
352
|
- `headerless` treats the first row as data (no header row)
|
|
347
|
-
-
|
|
353
|
+
- `auto` sets width to shrink-to-content
|
|
354
|
+
- A percentage value (e.g. `60%`, `33.3%`) sets an explicit percentage width
|
|
355
|
+
- A pixel value (e.g. `400px`) sets an explicit pixel width
|
|
356
|
+
- `center` centers the table (auto margins)
|
|
357
|
+
- `right` right-aligns the table (margin-left: auto)
|
|
358
|
+
- `left` left-aligns the table (default, no extra styles)
|
|
359
|
+
- Default width is 100%; default alignment is left
|
|
360
|
+
- All flags can be combined in any order
|
|
348
361
|
- Works with both Allman and K&R brace styles
|
|
349
362
|
}
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
{[table 60% center]
|
|
366
|
+
Endpoint | Status
|
|
367
|
+
/v2/weather | Active
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
{[table auto right borderless]
|
|
371
|
+
Key | Value
|
|
372
|
+
Version | 2.0
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
{[table 400px]
|
|
376
|
+
Name | Age
|
|
377
|
+
Alice | 30
|
|
378
|
+
}
|
|
379
|
+
```
|
|
350
380
|
}
|
|
351
381
|
}
|
|
352
382
|
|
|
@@ -387,6 +417,8 @@ Content of Section B.
|
|
|
387
417
|
```
|
|
388
418
|
|
|
389
419
|
Only `http`, `https`, and `mailto` schemes are recognised.
|
|
420
|
+
|
|
421
|
+
Bare URLs starting with `http://`, `https://`, or `mailto:` are also auto-linked without angle brackets.
|
|
390
422
|
}
|
|
391
423
|
|
|
392
424
|
# Images @images
|
|
@@ -466,7 +498,7 @@ Content of Section B.
|
|
|
466
498
|
|
|
467
499
|
# Escaping @escaping
|
|
468
500
|
{
|
|
469
|
-
In normal text (including headings and paragraphs), a backslash escapes: `\\` `\{` `\}` `\@` `\[` `\]` `\(` `\)` `\*` `\~` `\#` `\!` `\<` `\>` `\$` `\+` `\=` `\-` `\^` and `` \` ``.
|
|
501
|
+
In normal text (including headings and paragraphs), a backslash escapes: `\\` `\{` `\}` `\@` `\[` `\]` `\(` `\)` `\*` `\~` `\#` `\!` `\<` `\>` `\$` `\+` `\=` `\-` `\^` `\?` and `` \` ``.
|
|
470
502
|
|
|
471
503
|
Escapes are processed before reference detection.
|
|
472
504
|
|
|
@@ -516,6 +548,115 @@ Content of Section B.
|
|
|
516
548
|
}
|
|
517
549
|
}
|
|
518
550
|
}
|
|
551
|
+
|
|
552
|
+
# Scope Types @scope-types
|
|
553
|
+
{
|
|
554
|
+
A scope type annotation provides semantic meaning to a scope. The type is specified with `:typename` on the heading line, after optional `@id`:
|
|
555
|
+
|
|
556
|
+
```
|
|
557
|
+
# User Authentication @auth :requirement
|
|
558
|
+
{
|
|
559
|
+
The system shall authenticate users via OAuth 2.0.
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
# OAuth Flow :specification @oauth-flow
|
|
563
|
+
{
|
|
564
|
+
Implements @auth using the authorization code flow.
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
# Important :warning
|
|
568
|
+
{
|
|
569
|
+
This API is deprecated and will be removed in v3.0.
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
{[.]
|
|
574
|
+
- Syntax: `# Title @id :type` or `# Title :type @id` or `# Title :type` — both orderings of `@id` and `:type` are supported
|
|
575
|
+
- The colon in `:type` requires whitespace before it, so `# Note: Important` is NOT a scope type — the colon is part of the title text
|
|
576
|
+
- Any string is valid as a type name
|
|
577
|
+
- Well-known types: `schema`, `example`, `requirement`, `specification`, `definition`, `note`, `warning`, `test`, `task`, `api`, `config`, `deprecated`, `comment`
|
|
578
|
+
- The type is stored on the AST node and available to renderers and tooling
|
|
579
|
+
- Scope types enable AI agents to filter and navigate by semantic meaning (e.g., "show all requirements", "find tests for this spec")
|
|
580
|
+
- Works with K&R brace style: `# Title :warning {`
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
# Comment Scopes @comment-scopes
|
|
584
|
+
{
|
|
585
|
+
The `:comment` scope type creates a scope that is present in the AST but not rendered in the document output. This is useful for editorial annotations, AI agent instructions, and internal notes:
|
|
586
|
+
|
|
587
|
+
```
|
|
588
|
+
# TODO :comment
|
|
589
|
+
{
|
|
590
|
+
Rewrite this section after the API stabilises.
|
|
591
|
+
@alice please review the error handling.
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
# Agent Instructions :comment
|
|
595
|
+
{
|
|
596
|
+
When summarising this document, focus on the
|
|
597
|
+
requirements and skip the implementation details.
|
|
598
|
+
}
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
{[.]
|
|
602
|
+
- Comment scopes use the standard scope type system — no special syntax beyond `:comment`
|
|
603
|
+
- The scope is parsed into the AST (agents and tooling can extract it)
|
|
604
|
+
- The scope is not rendered in HTML, PDF, or other visual outputs
|
|
605
|
+
- Comment scopes can contain any valid SDOC content (paragraphs, lists, code blocks, nested scopes)
|
|
606
|
+
- Useful for structured annotations that are too complex for line comments
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
# Data Blocks @data-blocks
|
|
612
|
+
{
|
|
613
|
+
A code fence with the `:data` flag causes the parser to parse the content as structured data and attach it to the AST node:
|
|
614
|
+
|
|
615
|
+
`````
|
|
616
|
+
```json :data
|
|
617
|
+
{
|
|
618
|
+
"name": "SDOC",
|
|
619
|
+
"version": "0.2",
|
|
620
|
+
"features": ["scopes", "lists", "tables"]
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
`````
|
|
624
|
+
|
|
625
|
+
{[.]
|
|
626
|
+
- Syntax: ` ```json :data ` — the `:data` flag follows the language tag on the opening fence line
|
|
627
|
+
- The parser parses the JSON content and stores the result on the AST node
|
|
628
|
+
- Invalid JSON produces a parse error
|
|
629
|
+
- JSON is the only supported data format in v0.2
|
|
630
|
+
- Without the `:data` flag, JSON code blocks are treated as raw text (existing behaviour)
|
|
631
|
+
- Data blocks render visually the same as regular code blocks, but the parsed data is available to tooling and agents
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
# Line Comments @line-comments
|
|
636
|
+
{
|
|
637
|
+
A line starting with `//` (after optional indentation) is a line comment. The line is skipped entirely — it does not appear in the AST and is not rendered:
|
|
638
|
+
|
|
639
|
+
```
|
|
640
|
+
# Configuration @config
|
|
641
|
+
{
|
|
642
|
+
// TODO: add validation rules
|
|
643
|
+
The config file uses JSON format.
|
|
644
|
+
|
|
645
|
+
// This paragraph is hidden from output
|
|
646
|
+
// but visible in the source file.
|
|
647
|
+
|
|
648
|
+
See @setup for installation steps.
|
|
649
|
+
}
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
{[.]
|
|
653
|
+
- The `//` must be at the start of the line (after optional whitespace)
|
|
654
|
+
- Comment lines are discarded during parsing — they are not present in the AST
|
|
655
|
+
- Inside code blocks: `//` has no special meaning (code block content is raw)
|
|
656
|
+
- Mid-line `//` has no special meaning — URLs like `https://example.com` are safe
|
|
657
|
+
- Use line comments for quick annotations; use comment scopes (@comment-scopes) for structured non-rendered content
|
|
658
|
+
}
|
|
659
|
+
}
|
|
519
660
|
}
|
|
520
661
|
|
|
521
662
|
# Styles, Header, and Footer @styling
|
|
@@ -585,7 +726,7 @@ Content of Section B.
|
|
|
585
726
|
date: 2026-02-09
|
|
586
727
|
version: 1.0
|
|
587
728
|
status: Draft
|
|
588
|
-
sdoc-version: 0.
|
|
729
|
+
sdoc-version: 0.2
|
|
589
730
|
}
|
|
590
731
|
```
|
|
591
732
|
|
|
@@ -593,7 +734,7 @@ Content of Section B.
|
|
|
593
734
|
- Key matching is case-insensitive
|
|
594
735
|
- The pattern requires at least one space after the colon (`key: value`, not `key:value`)
|
|
595
736
|
- Well-known keys: `style`, `styleappend`/`style-append`, `header`, `footer`, `sdoc-version`
|
|
596
|
-
- `sdoc-version` identifies the SDOC format version the document targets (current: `0.
|
|
737
|
+
- `sdoc-version` identifies the SDOC format version the document targets (current: `0.2`). A parser warning is emitted when this key is missing.
|
|
597
738
|
- All other keys are stored as custom properties (e.g., `author`, `date`, `version`, `status`, `tags`)
|
|
598
739
|
- Sub-scope syntax takes precedence: if both `# Style { path }` and `style: path` exist, the sub-scope value wins
|
|
599
740
|
- Key:value and sub-scope syntax can be mixed freely in the same meta scope
|
|
@@ -713,6 +854,7 @@ Content of Section A.
|
|
|
713
854
|
- `>` blockquote line
|
|
714
855
|
- `---` / `***` / `___` horizontal rule
|
|
715
856
|
- `` ``` `` code fence
|
|
857
|
+
- `//` line comment (discarded, not in AST)
|
|
716
858
|
}
|
|
717
859
|
|
|
718
860
|
Blank lines are allowed anywhere and are ignored.
|
|
@@ -731,25 +873,34 @@ Content of Section A.
|
|
|
731
873
|
scope = heading ws? block
|
|
732
874
|
| heading ws? braceless_body
|
|
733
875
|
| heading_with_opener block_body "}" ;
|
|
734
|
-
heading = "#" { "#" } ws title
|
|
735
|
-
|
|
876
|
+
heading = "#" { "#" } ws title
|
|
877
|
+
((ws id)? (ws scope_type)? | (ws scope_type)? (ws id)?) ;
|
|
878
|
+
heading_with_opener = "#" { "#" } ws title
|
|
879
|
+
((ws id)? (ws scope_type)? | (ws scope_type)? (ws id)?) ws block_opener ;
|
|
736
880
|
id = "@" ident ;
|
|
881
|
+
scope_type = ":" ident ; (* id and scope_type may appear in either order *)
|
|
737
882
|
block_opener = "{" | "{[.]" | "{[#]" | table_open ;
|
|
738
883
|
block = "{" ws? block_body "}" ;
|
|
739
|
-
braceless_body = { paragraph | code_block |
|
|
740
|
-
|
|
|
741
|
-
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
884
|
+
braceless_body = { paragraph | code_block | data_block | blockquote
|
|
885
|
+
| implicit_list | horizontal_rule | headingless_scope
|
|
886
|
+
| list_scope | table_scope | bare_directive
|
|
887
|
+
| line_comment | blank } ;
|
|
888
|
+
|
|
889
|
+
bare_directive = "@" ("meta" | "about") (ws block | braceless_body) ;
|
|
890
|
+
block_body = { blank | line_comment | paragraph | scope
|
|
891
|
+
| headingless_scope | list_scope | table_scope
|
|
745
892
|
| implicit_list | blockquote | horizontal_rule
|
|
746
|
-
| code_block | comma_sep } ;
|
|
893
|
+
| code_block | data_block | bare_directive | comma_sep } ;
|
|
747
894
|
headingless_scope = "{" ws? block_body "}" ;
|
|
748
895
|
list_scope = list_open ws? list_body "}" ;
|
|
749
896
|
list_open = "{[.]" | "{[#]" ;
|
|
750
897
|
table_scope = table_open ws? table_body "}" ;
|
|
751
898
|
table_open = "{[table" { ws table_flag } "]" ;
|
|
752
|
-
table_flag = "borderless" | "headerless"
|
|
899
|
+
table_flag = "borderless" | "headerless"
|
|
900
|
+
| "auto" | percentage | pixels
|
|
901
|
+
| "left" | "center" | "right" ;
|
|
902
|
+
percentage = digit { digit } [ "." digit { digit } ] "%" ;
|
|
903
|
+
pixels = digit { digit } "px" ;
|
|
753
904
|
table_body = table_row { table_row } ;
|
|
754
905
|
table_row = cell { "|" cell } ;
|
|
755
906
|
list_body = { blank | comma_sep | scope | list_item_shorthand
|
|
@@ -767,15 +918,19 @@ Content of Section A.
|
|
|
767
918
|
paragraph = text_line { ws? text_line } ;
|
|
768
919
|
text_line = line_not_starting_with_command ;
|
|
769
920
|
|
|
770
|
-
blockquote = quote_line { quote_line
|
|
921
|
+
blockquote = quote_line { quote_line } ;
|
|
771
922
|
quote_line = ">" text_line ;
|
|
772
923
|
|
|
773
924
|
horizontal_rule = "---" | "***" | "___" ;
|
|
774
925
|
|
|
775
926
|
code_block = fence_open raw_text fence_close ;
|
|
927
|
+
data_block = data_fence_open raw_text fence_close ;
|
|
776
928
|
fence_open = "```" [lang] [ws "src:" path] [ws "lines:" range] newline ;
|
|
929
|
+
data_fence_open = "```" lang ws ":data" newline ;
|
|
777
930
|
fence_close = "```" newline ;
|
|
778
931
|
|
|
932
|
+
line_comment = "//" { any_char } newline ; (* discarded, not in AST *)
|
|
933
|
+
|
|
779
934
|
comma_sep = "," ;
|
|
780
935
|
blank = newline ;
|
|
781
936
|
|
|
@@ -783,7 +938,10 @@ Content of Section A.
|
|
|
783
938
|
```
|
|
784
939
|
|
|
785
940
|
{[.]
|
|
786
|
-
- `title` is the remainder of the heading line, excluding the optional trailing `@id`
|
|
941
|
+
- `title` is the remainder of the heading line, excluding the optional trailing `@id` and `:type`
|
|
942
|
+
- `scope_type` and `id` may appear in either order after the title
|
|
943
|
+
- `line_comment` lines are discarded during parsing and do not appear in the AST
|
|
944
|
+
- `data_block` content is parsed as JSON; invalid JSON produces a parse error
|
|
787
945
|
- If a line starts with a command token, it is not a paragraph line
|
|
788
946
|
- The grammar is line-oriented; practical parsers should operate on lines
|
|
789
947
|
}
|
|
@@ -792,10 +950,10 @@ Content of Section A.
|
|
|
792
950
|
# Open Questions @open-questions
|
|
793
951
|
{
|
|
794
952
|
{[.]
|
|
795
|
-
- Comment syntax (if any)
|
|
796
953
|
- Duplicate ID resolution (error vs warning vs nearest-scope)
|
|
797
|
-
- Additional list types (
|
|
954
|
+
- Additional list types (alpha, roman)
|
|
798
955
|
- Additional inline formatting (underline)
|
|
956
|
+
- Additional data block formats beyond JSON (YAML, TOML)
|
|
799
957
|
}
|
|
800
958
|
}
|
|
801
959
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@entropicwarrior/sdoc",
|
|
3
3
|
"displayName": "SDOC",
|
|
4
4
|
"description": "A plain-text documentation format with explicit brace scoping — deterministic parsing, AI-agent efficiency, and 10-50x token savings vs Markdown.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.2.0",
|
|
6
6
|
"publisher": "entropicwarrior",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
package/src/notion-renderer.js
CHANGED
|
@@ -246,6 +246,7 @@ function renderNotionBlocks(nodes) {
|
|
|
246
246
|
// Unwrap document scope wrapper (single root scope)
|
|
247
247
|
if (nodes.length === 1 && nodes[0].type === "scope" && nodes[0].children) {
|
|
248
248
|
const doc = nodes[0];
|
|
249
|
+
if (doc.scopeType === "comment") return [];
|
|
249
250
|
if (doc.hasHeading && doc.title) {
|
|
250
251
|
// Document title scope: render as top-level toggle heading
|
|
251
252
|
const childBlocks = renderChildren(doc.children, 2, 1);
|
|
@@ -290,6 +291,8 @@ function renderNode(node, depth, nestLevel) {
|
|
|
290
291
|
}
|
|
291
292
|
|
|
292
293
|
function renderScope(scope, depth, nestLevel) {
|
|
294
|
+
if (scope.scopeType === "comment") return [];
|
|
295
|
+
|
|
293
296
|
const level = Math.min(3, Math.max(1, depth));
|
|
294
297
|
|
|
295
298
|
if (scope.hasHeading === false) {
|