red_quilt 0.7.2 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 689739c1f6cf971cbeaacdbb65b50fcf7600cecef38bc25aa607ab5e87a559e5
4
- data.tar.gz: 2d57a5a5993bfb8352c18ec8c6da5dc06169d59198e72870399c85d4cc1e1769
3
+ metadata.gz: 56e09a19cfb78fad2a7e9a377f3ec6b9033d2160d4c126719e8220245b340709
4
+ data.tar.gz: 35bcb8de686345a72b9d2edf0261c7b201fb138047b571e3cc14b4f62be2671a
5
5
  SHA512:
6
- metadata.gz: b3f3b90749d7307db9121d9f1af968a72e94cad5aa06a3f9cf54505ff047e482246f5aa2c0fa14f1b74d9b3d03b516629b3f1c4e57e5ecb6cf5d171d4bc2b6f6
7
- data.tar.gz: f8a8929d8075e0c9e3ec32145daac57ca4565ff299bed3f119faee01f5253727d44acea4dacac579ee676715138e4f03f16e0555544b4b25ee1e30c779db1de5
6
+ metadata.gz: a5ad64e49f61ddcca8c3453b28ab06e7c04d76da574d82e6185b9a175189f313e043dcb586ff4293ad801b6bbb521ecca902f4dd9cac630186db97a2d8de5b91
7
+ data.tar.gz: fc3764a74f06f1d902afd5eeba1e572ddb97f3fa1c9306f4de937e1929937d22830f856c77b03d2e1fc658be3310d7f7535cb994a7945cf7f5f18ea52e0416c4
data/CHANGELOG.md CHANGED
@@ -9,6 +9,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
+ - `NodeRef#info`: returns the fence info string of a code block (e.g. `ruby`
13
+ in ` ```ruby `, or `vtt audio="x.mp3"`); `""` for code blocks without one
14
+ and for every other node type. The raw code content remains available via
15
+ `NodeRef#text`.
16
+ - `Renderer::HTML#render_fragment(nodes)`: renders an Array of `NodeRef` in
17
+ order and returns the HTML fragment without affecting the main render
18
+ output. Renderer state shared across nodes (e.g. the heading-id slugger) is
19
+ preserved between calls. Lets callers that partition a document render the
20
+ pieces separately without reaching into renderer internals.
21
+ - `Arena` semantic payload accessors (`heading_level`, `list_ordered?`,
22
+ `code_block_info`, `link_destination`, `footnote_number`, …) for callers
23
+ that walk `Document#arena`, replacing direct use of the raw `int1`/`str2`
24
+ columns.
25
+
26
+ ## [0.7.2] - 2026-06-23
27
+
28
+ ### Added
29
+
30
+ - Opt-in YAML frontmatter support via the `frontmatter:` option on `parse` /
31
+ `render_html` and the `--frontmatter` CLI flag (off by default). A leading
32
+ `---` … `---` block is removed from the rendered body and exposed as
33
+ `Document#frontmatter`; in standalone output its `title` / `lang` keys fill
34
+ in `<title>` / `<html lang>`.
12
35
  - Opt-in Mermaid diagram support via the `mermaid:` option on `render_html` /
13
36
  `Document#to_html` and the `--mermaid` CLI flag (off by default). Fenced
14
37
  ` ```mermaid ` code blocks render as `<pre class="mermaid">` containers; in
data/README.md CHANGED
@@ -48,6 +48,7 @@ RedQuilt.render_html("Hi <em>tag</em>", allow_html: true)
48
48
  | `extended_autolinks:` | `false` | GFM: linkify bare `http(s)://` / `www.` / email addresses |
49
49
  | `footnotes:` | `false` | GFM footnotes (see below) |
50
50
  | `lint:` | `false` | Collect lint diagnostics (empty links, missing image alt, heading-level skips) |
51
+ | `frontmatter:` | `false` | Parse leading YAML frontmatter (`---`) as metadata (see below) |
51
52
 
52
53
  ### Footnotes (opt-in)
53
54
 
@@ -100,6 +101,41 @@ RedQuilt.parse("```mermaid\ngraph LR\n A --> B\n```")
100
101
  In standalone output each diagram is made interactive with
101
102
  [svg-pan-zoom](https://github.com/bumbu/svg-pan-zoom) (loaded from a CDN).
102
103
 
104
+ ### YAML frontmatter (opt-in)
105
+
106
+ `parse` / `render_html` accept `frontmatter:` to extract a leading YAML
107
+ frontmatter block (the `---` … `---` fences used by Jekyll, Hugo).
108
+ The block is parsed with `Psych.safe_load` and removed from the rendered body;
109
+ the parsed Hash is exposed as `Document#frontmatter`.
110
+
111
+ ```ruby
112
+ doc = RedQuilt.parse(<<~MD, frontmatter: true)
113
+ ---
114
+ title: My Page
115
+ lang: ja
116
+ ---
117
+
118
+ # Body
119
+ MD
120
+ doc.frontmatter # => {"title" => "My Page", "lang" => "ja"}
121
+ doc.to_html # => "<h1>Body</h1>\n" (frontmatter stripped)
122
+ ```
123
+
124
+ In standalone output the frontmatter's `title` / `lang` fill in `<title>` /
125
+ `<html lang>` when no explicit argument is given (explicit argument >
126
+ frontmatter > default):
127
+
128
+ ```ruby
129
+ doc.to_html(standalone: true)
130
+ # <html lang="ja"> … <title>My Page</title> …
131
+ ```
132
+
133
+ The feature is opt-in, so a bare `---` is never mistaken for frontmatter
134
+ unless `frontmatter: true` is passed. Frontmatter lines are blanked rather
135
+ than deleted, so body source spans and diagnostic line numbers stay relative
136
+ to the start of the file. Invalid YAML records a `:frontmatter` warning
137
+ diagnostic and leaves `Document#frontmatter` as `nil` without raising.
138
+
103
139
  ### Tilt integration
104
140
 
105
141
  RedQuilt ships a [Tilt](https://github.com/jeremyevans/tilt) adapter.
@@ -186,6 +222,8 @@ redquilt --mermaid --open input.md
186
222
  Dir.tmpdir when -o is not given)
187
223
  --mermaid Render `mermaid` code blocks as diagrams (loads
188
224
  mermaid.js from a CDN in standalone output)
225
+ --frontmatter Parse leading YAML frontmatter (---) as metadata;
226
+ fills <title>/lang in standalone output
189
227
  --diagnostics Print diagnostics to stderr
190
228
  --diagnostics-only Print diagnostics only (suppress output)
191
229
  -h, --help Show help
data/docs/api.md CHANGED
@@ -18,6 +18,7 @@ doc.source_map # Line/column lookup (lazy memoized)
18
18
  doc.diagnostics # Array of RedQuilt::Diagnostic collected while parsing
19
19
  doc.allow_html? # Check HTML pass-through setting
20
20
  doc.disallow_raw_html? # Check GFM disallowed-raw-HTML filtering setting
21
+ doc.frontmatter # Parsed YAML frontmatter Hash, or nil (see below)
21
22
 
22
23
  # Standalone document with an embedded theme:
23
24
  doc.to_html(standalone: true, theme: :default, title: "My Doc", lang: "en")
@@ -27,6 +28,15 @@ doc.to_html(standalone: true, theme: :default, title: "My Doc", lang: "en")
27
28
  # Render `mermaid` code blocks as <pre class="mermaid"> diagrams; in
28
29
  # standalone mode the mermaid.js runtime is loaded from a CDN too.
29
30
  doc.to_html(standalone: true, mermaid: true)
31
+
32
+ # Parse a leading YAML frontmatter block (--- ... ---). Off by default; when
33
+ # enabled the block is removed from the rendered body and exposed as a Hash.
34
+ doc = RedQuilt.parse("---\ntitle: Hi\nlang: ja\n---\n\n# Body", frontmatter: true)
35
+ doc.frontmatter # => {"title" => "Hi", "lang" => "ja"} (nil when absent/disabled)
36
+ # In standalone output frontmatter title/lang fill <title>/<html lang> unless
37
+ # an explicit argument overrides them. Invalid YAML adds a :frontmatter
38
+ # warning diagnostic and leaves doc.frontmatter as nil.
39
+ doc.to_html(standalone: true)
30
40
  ```
31
41
 
32
42
  ## NodeRef (AST node wrapper)
@@ -40,6 +50,7 @@ node.children # Array[NodeRef]
40
50
  node.walk # Enumerator[NodeRef] or { |node| ... } block
41
51
  node.find_all(:link) # Array[NodeRef] with matching type
42
52
  node.text # String (concatenated child text)
53
+ node.info # String fence info of a code block (e.g. "ruby")
43
54
 
44
55
  # Position information (byte offset)
45
56
  node.source_span # SourceSpan with start_byte, end_byte
@@ -231,6 +231,102 @@ module RedQuilt
231
231
  @str2[id]
232
232
  end
233
233
 
234
+ # --- Semantic payload accessors -------------------------------------
235
+ #
236
+ # int1..int3 / str1 / str2 are anonymous columns; their meaning is
237
+ # per-NodeType (see the class comment). These readers are the single
238
+ # source of truth for those conventions, so callers (renderers, the
239
+ # NodeRef wrapper, AST/MDAST export) never need to know which raw
240
+ # column a field lives in. Writers use add_node's keyword args,
241
+ # update_*, or a small set of typed writers (e.g.
242
+ # #resolve_footnote_definition) when the intent is worth naming.
243
+ #
244
+ # The reader is responsible for calling these only on the matching
245
+ # NodeType; on a mismatching node they return whatever the raw column
246
+ # holds (typically the 0 / nil default).
247
+
248
+ # HEADING: nesting level (1..6).
249
+ def heading_level(id)
250
+ @int1[id]
251
+ end
252
+
253
+ # LIST: ordered (`1.`) vs bullet (`-`).
254
+ def list_ordered?(id)
255
+ @int1[id] == 1
256
+ end
257
+
258
+ # LIST: the start number of an ordered list (1 unless overridden).
259
+ def list_start(id)
260
+ @int2[id]
261
+ end
262
+
263
+ # LIST: tight (no blank lines between items) vs loose.
264
+ def list_tight?(id)
265
+ @int3[id] == 1
266
+ end
267
+
268
+ # LIST: the item delimiter as authored (e.g. "-", "1.").
269
+ def list_delimiter(id)
270
+ @str1[id]
271
+ end
272
+
273
+ # TABLE_ROW: header row (rendered in <thead>) vs body row.
274
+ def table_row_header?(id)
275
+ @int1[id] == 1
276
+ end
277
+
278
+ # TABLE_CELL: header cell (<th>) vs data cell (<td>).
279
+ def table_cell_header?(id)
280
+ @int1[id] == 1
281
+ end
282
+
283
+ # CODE_BLOCK: the fence info string (e.g. 'ruby', 'vtt audio="x"').
284
+ def code_block_info(id)
285
+ @str2[id]
286
+ end
287
+
288
+ # LINK / IMAGE: destination URL.
289
+ def link_destination(id)
290
+ @str1[id]
291
+ end
292
+
293
+ # LINK / IMAGE: optional title attribute (nil/empty when absent).
294
+ def link_title(id)
295
+ @str2[id]
296
+ end
297
+
298
+ # FOOTNOTE_REFERENCE: the assigned footnote number.
299
+ def footnote_number(id)
300
+ @int1[id]
301
+ end
302
+
303
+ # FOOTNOTE_REFERENCE: which occurrence of a repeated reference this is
304
+ # (1-based), used to give each backref a unique anchor.
305
+ def footnote_occurrence(id)
306
+ @int2[id]
307
+ end
308
+
309
+ # FOOTNOTE_REFERENCE / FOOTNOTE_DEFINITION: the author-written label.
310
+ def footnote_label(id)
311
+ @str1[id]
312
+ end
313
+
314
+ # FOOTNOTE_DEFINITION: total number of references to this footnote,
315
+ # materialized by FootnotePass so renderers can read it off the node
316
+ # instead of consulting the registry. (FOOTNOTE_REFERENCE reuses int2
317
+ # for its own occurrence index; see #footnote_occurrence.)
318
+ def footnote_total_references(id)
319
+ @int2[id]
320
+ end
321
+
322
+ # FOOTNOTE_DEFINITION: records the resolved number and total reference
323
+ # count onto the node (read back via #footnote_number /
324
+ # #footnote_total_references).
325
+ def resolve_footnote_definition(id, number, total_references)
326
+ @int1[id] = number
327
+ @int2[id] = total_references
328
+ end
329
+
234
330
  # Returns a SourceSpan for the node, or nil when the node has no
235
331
  # span (source_start < 0, meaning the content is held in str1).
236
332
  def source_span(id)