@rse/specbook 1.3.2 → 1.4.1

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.
Files changed (50) hide show
  1. package/README.md +9 -2
  2. package/dst/specbook-api.d.ts +5 -1
  3. package/dst/specbook-api.js +23 -9
  4. package/dst/specbook-cli.js +8 -5
  5. package/dst/specbook-cmd-export.js +2 -2
  6. package/dst/specbook-cmd-init.js +16 -2
  7. package/dst/specbook-coverage.js +1 -1
  8. package/dst/specbook-diagram.d.ts +1 -1
  9. package/dst/specbook-diagram.js +12 -10
  10. package/dst/specbook-export-ast.d.ts +1 -1
  11. package/dst/specbook-export-ast.js +21 -4
  12. package/dst/specbook-export-code.d.ts +14 -0
  13. package/dst/specbook-export-code.js +155 -0
  14. package/dst/specbook-export-common.d.ts +8 -2
  15. package/dst/specbook-export-common.js +126 -6
  16. package/dst/specbook-export-diagram.d.ts +15 -0
  17. package/dst/specbook-export-diagram.js +211 -0
  18. package/dst/specbook-export-html-fold.d.ts +20 -0
  19. package/dst/specbook-export-html-fold.js +458 -0
  20. package/dst/specbook-export-html-render.d.ts +51 -0
  21. package/dst/specbook-export-html-render.js +748 -0
  22. package/dst/specbook-export-html-search.js +6 -5
  23. package/dst/specbook-export-html-template.d.ts +21 -0
  24. package/dst/specbook-export-html-template.js +282 -0
  25. package/dst/specbook-export-html-widget.d.ts +12 -0
  26. package/dst/specbook-export-html-widget.js +605 -0
  27. package/dst/specbook-export-html.css +325 -50
  28. package/dst/specbook-export-html.js +62 -1931
  29. package/dst/specbook-export-image.d.ts +2 -0
  30. package/dst/specbook-export-image.js +161 -9
  31. package/dst/specbook-export-md.js +60 -12
  32. package/dst/specbook-format-schema.d.ts +1 -1
  33. package/dst/specbook-format-schema.js +1 -1
  34. package/dst/specbook-format-spec.d.ts +1 -0
  35. package/dst/specbook-format-spec.js +1 -0
  36. package/dst/specbook-format.md +173 -19
  37. package/dst/specbook-format.yaml +265 -24
  38. package/dst/specbook-ignore.js +2 -0
  39. package/dst/specbook-link.js +21 -5
  40. package/dst/specbook-mcp.js +9 -5
  41. package/dst/specbook-parse-common.d.ts +24 -0
  42. package/dst/specbook-parse-common.js +137 -10
  43. package/dst/specbook-parse-semantic.js +2 -0
  44. package/dst/specbook-parse-syntax.js +81 -20
  45. package/dst/specbook-parse.js +1 -1
  46. package/dst/specbook-project.d.ts +1 -0
  47. package/dst/specbook-project.js +10 -1
  48. package/dst/specbook-theme.d.ts +1 -0
  49. package/dst/specbook-theme.js +2 -0
  50. package/package.json +18 -12
@@ -123,7 +123,7 @@ type SchemaDiagram = {
123
123
  config?: Partial<GradiaConfig>
124
124
  }
125
125
  type SchemaFormat = {
126
- type?: "auto" | "complex" | "concise"
126
+ type?: "auto" | "complex" | "compact" | "concise"
127
127
  maxTableColumns?: number
128
128
  maxCellHeight?: number
129
129
  withUnusedProps?: boolean
@@ -323,7 +323,8 @@ type SchemaGradiaConfig = Partial<{
323
323
  - `SchemaDiagram.center?: string | SchemaDiagramCenter`:
324
324
  object a `hub` diagram is projected onto (default: `self`), or a
325
325
  synthetic center node, labeled from a referenced `source` object (or
326
- one of its properties, via `property`) or a literal `label`, typed
326
+ one of its properties, via `property`, falling back onto the `label`
327
+ or the object name while absent) or a literal `label`, typed
327
328
  (under `qualified`) by `kind`, and linked to the `source` object,
328
329
  BECAUSE the focus of a hub is not always a specification object --
329
330
  the solution itself, e.g., sits in the middle of a context diagram
@@ -429,8 +430,9 @@ type SchemaGradiaConfig = Partial<{
429
430
  HTML/PDF rendering of the objects of an object kind among their siblings,
430
431
  BECAUSE one uniform rendering fits neither rich nor numerous objects
431
432
 
432
- - `SchemaFormat.type?: "auto" | "complex" | "concise"`:
433
- rendering: `complex` (sections), `concise` (tables), or `auto`,
433
+ - `SchemaFormat.type?: "auto" | "complex" | "compact" | "concise"`:
434
+ rendering: `complex` (sections), `concise` (tables), `compact`
435
+ (tables of section-like cells), or `auto`,
434
436
  BECAUSE sections bury small objects, tables truncate rich ones
435
437
 
436
438
  - `SchemaFormat.maxTableColumns?: number`:
@@ -571,7 +573,8 @@ Instead of naming an existing object, `center` can also declare a
571
573
  *synthetic* center node, which represents something no specification
572
574
  object models -- like the solution itself in the middle of a context
573
575
  diagram. Such a center is labeled from a referenced `source` object (or
574
- one of its properties, via `property`) or a literal `label`, and links
576
+ one of its properties, via `property`, falling back onto the `label` or
577
+ the object name while the property is absent) or a literal `label`, and links
575
578
  to the `source` object. As the nodes carry no `[[xxx]]` reference to a
576
579
  synthetic center, `centerEdges` synthesizes the edges from a direction
577
580
  `property` of the node objects: its `inbound` value maps onto a
@@ -649,15 +652,20 @@ how the HTML/PDF export renders the objects of that kind among their
649
652
  siblings, so that the sibling kinds below one parent can render
650
653
  differently (e.g. one kind as a table, another as sections). Its `type`
651
654
  selects the rendering: `complex` (nested sections), `concise` (one
652
- compact table of all sibling objects of the kind), or the default
653
- `auto`, which collapses only the deepest level into tables.
655
+ compact table of all sibling objects of the kind), `compact` (one
656
+ table of the name and a single cell per object, which renders the
657
+ object like `complex`, but without its heading: diagram, properties as
658
+ key/value lines, and description, for a kind whose many properties a
659
+ table row cannot hold), or the default `auto`, which collapses only the
660
+ deepest level into tables.
654
661
 
655
662
  Inside a `concise` table, unconfigured child kinds implicitly stay
656
663
  `concise` and render as sub-tables inside the description cells, while
657
664
  an explicitly configured `type` is always honored, even a `complex`
658
665
  rendering pressed into a cell. `maxTableColumns` (default: `4`) bounds
659
666
  the columns of the compact table of the kind (a wider group chunks
660
- its properties into embedded per-object tables), `maxCellHeight`
667
+ its properties into embedded per-object tables, while a `compact`
668
+ one ignores it), `maxCellHeight`
661
669
  (default: `40`) is the percentage a table cell of the kind may exceed
662
670
  the height of every other non-empty cell of its row before the HTML
663
671
  export folds its remaining text away, and `withUnusedProps`
@@ -702,6 +710,7 @@ type SpecObject = {
702
710
  type SpecDescription = {
703
711
  description: string
704
712
  rationale?: string
713
+ elaboration?: string
705
714
  embedding?: string[]
706
715
  }
707
716
  type SpecProperty = {
@@ -776,7 +785,7 @@ type SpecProperty = {
776
785
  BECAUSE a specification is a tree, not a flat list of statements
777
786
 
778
787
  - `SpecDescription`:
779
- description of an object: statement, rationale, and images,
788
+ description of an object: statement, rationale, elaboration, and images,
780
789
  BECAUSE the WHAT and the WHY are stored apart to be rendered apart
781
790
 
782
791
  - `SpecDescription.description: string`:
@@ -785,11 +794,18 @@ type SpecProperty = {
785
794
 
786
795
  - `SpecDescription.rationale?: string`:
787
796
  rationale text behind the `, BECAUSE ` split (the WHY),
797
+ up to the end of its paragraph,
788
798
  BECAUSE a statement without its WHY cannot be judged or revised
789
799
 
800
+ - `SpecDescription.elaboration?: string`:
801
+ blocks following the paragraph of the rationale (e.g. images or diagrams),
802
+ BECAUSE further content must not end up inside the WHY
803
+
790
804
  - `SpecDescription.embedding?: string[]`:
791
- image files embedded via `![xxx](yyy)`, inlined at parse time,
792
- one entry per file in markup order (empty for an unreadable file),
805
+ image, diagram source, and source code files embedded via `![xxx](yyy)`,
806
+ inlined at parse time, one entry per file in markup order (those of
807
+ the statement first, those of the elaboration second, and empty for
808
+ an unreadable file),
793
809
  BECAUSE the exports have to stand alone, without the image files
794
810
 
795
811
  - `SpecProperty`:
@@ -805,7 +821,8 @@ type SpecProperty = {
805
821
  BECAUSE constraints and references are checked against this text
806
822
 
807
823
  - `SpecProperty.embedding?: string[]`:
808
- image files embedded in the value, inlined at parse time,
824
+ image, diagram source, and source code files embedded in the value,
825
+ inlined at parse time,
809
826
  one entry per file in markup order (empty for an unreadable file),
810
827
  BECAUSE the exports have to stand alone, without the image files
811
828
 
@@ -992,10 +1009,15 @@ paragraphs, blockquotes, ordered lists (e.g. scenario steps or test
992
1009
  case procedures), and fenced code blocks. A fenced code block of
993
1010
  language `gradia` is skipped, as it is the derived diagram which
994
1011
  **SpecBook** itself emits into exported Markdown and which must not
995
- become authored content on a re-parse. In the Concise Format, the
996
- description is formed by the non-property segments of the item, so a
997
- description carrying a `;` cannot be a segment either and forces its
998
- object into the Complex Format.
1012
+ become authored content on a re-parse. A fenced code block of language
1013
+ `mermaid` (alias `mmd`) or `d2` is a [Mermaid](https://mermaid.js.org/)
1014
+ or [D2](https://d2lang.com/) diagram, which the HTML/PDF export renders
1015
+ as a diagram instead of as code (see "Diagram Embeddings" below). A
1016
+ fenced code block of any other known language is a syntax-highlighted
1017
+ code listing (see "Code Listings" below). In
1018
+ the Concise Format, the description is formed by the non-property
1019
+ segments of the item, so a description carrying a `;` cannot be a
1020
+ segment either and forces its object into the Complex Format.
999
1021
 
1000
1022
  A description is split into its *statement* and *rationale* at the first
1001
1023
  `, BECAUSE ` (or `, **BECAUSE** `) occurrence:
@@ -1005,10 +1027,25 @@ The event is created and configured but not visible to attendees,
1005
1027
  BECAUSE an event needs a private setup phase.
1006
1028
  ```
1007
1029
 
1030
+ The rationale ends with its paragraph: all blocks following it (further
1031
+ paragraphs, embedded images, and diagrams) form the *elaboration*, which
1032
+ is rendered below the statement and its rationale, so the description
1033
+ of a Complex Format object can state its WHAT and WHY first and
1034
+ illustrate them afterwards:
1035
+
1036
+ ````
1037
+ The event is created and configured but not visible to attendees,
1038
+ BECAUSE an event needs a private setup phase.
1039
+
1040
+ ![Event Lifecycle](event-lifecycle.mmd)
1041
+ ````
1042
+
1008
1043
  ### Wiki-Style References
1009
1044
 
1010
1045
  A `[[<reference/>]]` anywhere in a name, property value, description, or
1011
- rationale is a Wiki-style reference to another object, which is checked
1046
+ rationale (except inside a fenced code block, whose code stays verbatim,
1047
+ so e.g. a shell test `[[ -f x ]]` is no reference) is a Wiki-style
1048
+ reference to another object, which is checked
1012
1049
  for unique resolvability and rendered as a hyperlink in the HTML/PDF
1013
1050
  exports. A reference is a `.`-separated path of segments, where each
1014
1051
  segment matches a single object as:
@@ -1046,7 +1083,21 @@ SVG files are inlined as-is; PNG/JPEG/WebP files are embedded as base64
1046
1083
  `data:` URLs. URLs and other file types are left untouched. The
1047
1084
  HTML/PDF export optimizes the embedded images on its own (downscaled
1048
1085
  and re-encoded PNG/JPEG/WebP, minified SVG), so the files can stay in their
1049
- original resolution.
1086
+ original resolution. Like a diagram, an embedded image (except the logo
1087
+ of the title page) folds, maximizes, and is omitted (`-O diagram:image`,
1088
+ or by nesting level) in the HTML/PDF export, while the AST exports keep
1089
+ it.
1090
+
1091
+ A page of a PDF document is embedded like an image --
1092
+ `![<alt/>](<file/>.pdf)` for the first page, or
1093
+ `![<alt/>](<file/>.pdf#page=<n/>)` for the 1-based page number `<n/>` --
1094
+ where the document is inlined at parse time. The exports convert the
1095
+ page through `pdfjs-dist` into a vector SVG with its texts as glyph
1096
+ outlines and then treat it exactly like an embedded SVG image, while a
1097
+ page failing to render (e.g. an absent page number) is omitted with a
1098
+ notice. A presentation (e.g. PowerPoint), a spreadsheet (e.g. Excel), or
1099
+ any other office document is embedded through its PDF export, which
1100
+ carries its exact rendering and fonts.
1050
1101
 
1051
1102
  A reference carrying the `{theme}` placeholder -- e.g.
1052
1103
  `![Logo](logo-{theme}.svg)` -- is a *theme-aware* embedding: it expands
@@ -1056,10 +1107,113 @@ HTML export shows just the variant matching the color theme currently
1056
1107
  active in the document, while the PDF export (like print in general)
1057
1108
  always uses the `light` variant.
1058
1109
 
1110
+ An image without a dark variant is treated on the dark theme of the HTML
1111
+ export as its `dark` fragment parameter says (see "Fragment Parameters"
1112
+ below), while print always keeps it as it is. An image defaults to
1113
+ `dark=auto`, which inverts it (with its hues rotated back) if it most
1114
+ likely draws dark lines onto a transparent or light canvas: an SVG if it
1115
+ neither adapts itself (via `light-dark()` colors, `prefers-color-scheme`
1116
+ rules, or a `color-scheme` declaration, which follows the color theme of
1117
+ the document anyway), nor embeds a raster image, nor paints a background
1118
+ which is not light, and a raster image (PNG/JPEG/WebP) if at least 60%
1119
+ of its pixels are transparent or near-white and the others are dark on
1120
+ average and of a few colors only (unlike a photo or a colorful
1121
+ screenshot). As the detection cannot tell a logo or illustration with
1122
+ brand colors from a line drawing, `#dark=none` keeps such an image as it
1123
+ is, while `#dark=invert` forces the inversion (e.g.
1124
+ `![Overview](overview.svg#dark=invert)`). A
1125
+ [draw.io](https://www.drawio.com/) diagram is best embedded as its
1126
+ editable SVG (`.drawio.svg`, which draw.io keeps current on every save)
1127
+ with its "Adaptive Colors" page setting on "Automatic", as it then
1128
+ adapts all its colors itself. A theme-aware embedding ignores the
1129
+ fragment, as its `dark` variant already serves the dark theme.
1130
+
1059
1131
  The reference-style image `![<alt/>][<label/>]` embeds the image of its
1060
1132
  definition `[<label/>]: data:image/<type/>;base64,<data/>` (SVG, PNG,
1061
- JPEG, or WebP) in the same file, the only supported link definition.
1133
+ JPEG, or WebP, where a `;dark=invert` or `;dark=none` parameter behind
1134
+ the type carries the treatment on the dark theme, which the export
1135
+ always resolves for an SVG) in the same file, the only supported link
1136
+ definition.
1062
1137
  This is the form the normalized Markdown export emits for every embedded
1063
1138
  image (a theme-aware one by its `light` variant), with the definitions
1064
1139
  at the end of the document, so it stands alone without the image files.
1065
1140
  Author the inline form; the reference-style one is for re-parsing only.
1141
+
1142
+ ### Diagram Embeddings
1143
+
1144
+ A [Mermaid](https://mermaid.js.org/) or [D2](https://d2lang.com/)
1145
+ diagram is either written inline into a description as a fenced code
1146
+ block of language `mermaid` (alias `mmd`) or `d2`, or kept in a file of
1147
+ its own and embedded like an image -- `![<alt/>](<file/>.mmd)`,
1148
+ `![<alt/>](<file/>.mermaid)`, or `![<alt/>](<file/>.d2)` -- inside a
1149
+ description or property value, where its source is inlined at parse time.
1150
+ The HTML/PDF export renders every such diagram (Mermaid through
1151
+ `beautiful-mermaid`, D2 through `@terrastruct/d2`) in the theme colors of
1152
+ the document, once per color theme, like a theme-aware image. It folds,
1153
+ maximizes, and is omitted (`-O diagram:code`, for diagrams as code, or
1154
+ by nesting level) like a derived Gradia diagram. A diagram failing to render
1155
+ is omitted with a notice. The normalized Markdown and the AST exports
1156
+ carry the source of a fenced diagram as part of the description, and an
1157
+ embedded diagram file as its rendered SVG (the `light` variant).
1158
+
1159
+ ### Code Listings
1160
+
1161
+ A code snippet is either written inline into a description as a fenced
1162
+ code block of a known language (any language id or alias of
1163
+ [Shiki](https://shiki.style/), like `ts`, `java`, `css`, or `html`), or
1164
+ kept in a source code file of its own and embedded like an image --
1165
+ `![<alt/>](<file/>.<ext/>)`, where `<ext/>` is such a language id or
1166
+ alias (e.g. `.ts`, `.java`, `.css`, `.html`) -- inside a description or
1167
+ property value, where its source is inlined at parse time. A fenced code
1168
+ block of no or an unknown language stays plain preformatted text.
1169
+
1170
+ The HTML/PDF export renders every such snippet as a *code listing* in
1171
+ DejaVu Sans Mono, with line numbers and with its keywords, operators,
1172
+ and punctuation in the accent color and bold, its literals in the signal
1173
+ color, and its comments in the dim color and italic. A code listing
1174
+ folds, maximizes, and is omitted (`-O diagram:listing`, or by nesting
1175
+ level) like a diagram.
1176
+
1177
+ A fenced code listing takes whitespace-separated parameters behind its
1178
+ language, and an embedded one takes them as its fragment (see "Fragment
1179
+ Parameters" below):
1180
+
1181
+ - `start=<n/>` (fenced only): the number of the first line (default `1`).
1182
+ - `lines=<a/>-<b/>` or `lines=<a/>` (embedded only): the excerpt of the
1183
+ 1-based lines `<a/>` to `<b/>` (or line `<a/>` alone), numbered from
1184
+ `<a/>`, where an empty range is reported as an error.
1185
+ - `mark=<a/>[-<b/>][,...]`: the lines or line ranges to mark (in the
1186
+ shown line numbers) on a very light signal background.
1187
+
1188
+ ````
1189
+ ```ts start=10 mark=12-13
1190
+ const greeter = new Greeter("World")
1191
+ ```
1192
+
1193
+ ![Greeter Class](greeter.ts#lines=5-20&mark=8,12-14)
1194
+ ````
1195
+
1196
+ The AST exports carry an embedded source code file as its
1197
+ `data:text/x-code;lang=<lang/>;start=<n/>[;mark=<hunks/>];base64,<data/>`
1198
+ URL, while the normalized Markdown export turns one of a description into
1199
+ a fenced code block (with its `start` and `mark` parameters) at the end
1200
+ of the elaboration, and keeps one of a property value as a
1201
+ reference-style image definition of that URL.
1202
+
1203
+ #### Fragment Parameters
1204
+
1205
+ The fragment of an embedding reference is a list of `<key/>=<value/>`
1206
+ parameters joined by `&`, in any order and each at most once, with the
1207
+ values optionally URI-encoded (e.g. `%20` for a space): `page`
1208
+ (documents), `dark` (documents and images), and `lines` and `mark`
1209
+ (source code files, see "Code Listings" above). A fragment carrying any
1210
+ other key or an invalid value is reported as an error. The `dark`
1211
+ parameter selects the treatment on the dark theme of the HTML export,
1212
+ while print always shows the original: `dark=invert` inverts the
1213
+ embedding with its hues rotated back, `dark=none` shows it as it is, and
1214
+ `dark=auto` (the default) decides by the content of the image or the
1215
+ rendered document page (inverting a light canvas with dark ink, but
1216
+ neither a dark one nor one carrying a photo), as an image and a document
1217
+ page can be anything (line drawings, photos, logos); e.g.
1218
+ `![<alt/>](<file/>.pdf#page=3&dark=none)` keeps a page which the
1219
+ detection would invert, but whose colors have to stay.