markdown_composer 0.7.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 +7 -0
- data/CHANGELOG.md +23 -0
- data/LICENSE.txt +21 -0
- data/README.md +278 -0
- data/ROADMAP.md +80 -0
- data/docs/_md_composer_architecture.md +50 -0
- data/docs/_md_composer_cheatsheet.md +72 -0
- data/docs/_md_composer_concepts.md +64 -0
- data/docs/_md_composer_dev_guide.md +55 -0
- data/docs/_md_composer_getting_started.md +114 -0
- data/docs/_md_composer_readme.md +93 -0
- data/docs/_md_composer_user_guide.md +65 -0
- data/docs/ai/md_composer_ai_audit.md +35 -0
- data/docs/ai/md_composer_ai_canonical_docs.md +44 -0
- data/docs/ai/md_composer_ai_source_map.md +39 -0
- data/docs/compose/md_composer_compose_actions.md +338 -0
- data/docs/compose/md_composer_compose_anatomy.md +156 -0
- data/docs/compose/md_composer_compose_buffer.md +81 -0
- data/docs/compose/md_composer_compose_examples.md +31 -0
- data/docs/compose/md_composer_compose_include.md +136 -0
- data/docs/compose/md_composer_compose_select.md +198 -0
- data/docs/compose/md_composer_compose_sources.md +161 -0
- data/docs/compose/md_composer_compose_targets.md +194 -0
- data/docs/examples/md_composer_example_basic_compose.md +57 -0
- data/docs/examples/md_composer_example_buffer_target_actions.md +83 -0
- data/docs/examples/md_composer_example_fixtures.md +62 -0
- data/docs/examples/md_composer_example_html_output.md +50 -0
- data/docs/examples/md_composer_example_modify.md +77 -0
- data/docs/examples/md_composer_example_multi_row_compose.md +67 -0
- data/docs/examples/md_composer_example_ruby_plans.md +62 -0
- data/docs/examples/md_composer_example_structured_data.md +68 -0
- data/docs/examples/md_composer_example_transforms.md +68 -0
- data/docs/examples/md_composer_example_yaml_json_rows.md +56 -0
- data/docs/examples/md_composer_examples_readme.md +45 -0
- data/docs/examples/md_composer_runnable_examples.md +374 -0
- data/docs/examples/md_composer_source_ruby_dsl.md +88 -0
- data/docs/reference/md_composer_nested.md +170 -0
- data/docs/reference/md_composer_reference_api.md +71 -0
- data/docs/reference/md_composer_reference_capabilities.md +63 -0
- data/docs/reference/md_composer_reference_diagnostics.md +54 -0
- data/docs/reference/md_composer_reference_plan_schema.md +75 -0
- data/docs/reference/md_composer_reference_registries.md +63 -0
- data/docs/reference/md_composer_take.md +221 -0
- data/docs/reference/md_composer_unit_tokens.md +228 -0
- data/docs/reference/md_composer_where.md +227 -0
- data/docs/transform/md_composer_transform_anatomy.md +112 -0
- data/docs/transform/md_composer_transform_examples.md +30 -0
- data/docs/transform/md_composer_transform_modes.md +83 -0
- data/docs/transform/md_composer_transform_options.md +142 -0
- data/docs/transform/md_composer_transform_scope.md +97 -0
- data/docs/transform/md_composer_transform_transforms.md +99 -0
- data/examples/README.md +20 -0
- data/examples/advanced_composer.rb +207 -0
- data/examples/basic_compose.rb +24 -0
- data/examples/complex_composer.rb +235 -0
- data/examples/example_support.rb +18 -0
- data/examples/fixtures/current.md +179 -0
- data/examples/fixtures/faq.md +58 -0
- data/examples/fixtures/guide.md +62 -0
- data/examples/fixtures/site_intro.md +29 -0
- data/examples/fixtures/source.html +22 -0
- data/examples/html_input.rb +26 -0
- data/examples/output/advanced_composer.md +76 -0
- data/examples/output/basic_compose.md +25 -0
- data/examples/output/complex_composer.md +85 -0
- data/examples/output/html_input.md +4 -0
- data/examples/output/source_list_dsl.md +126 -0
- data/examples/output/standard_composer.md +46 -0
- data/examples/output/standard_sources_buffer.md +31 -0
- data/examples/output/yaml_plan.md +43 -0
- data/examples/plans/basic.yml +20 -0
- data/examples/source_list_dsl.rb +41 -0
- data/examples/standard_composer.rb +42 -0
- data/examples/standard_sources_buffer.rb +62 -0
- data/examples/yaml_plan.rb +17 -0
- data/lib/markdown_composer/capabilities.rb +223 -0
- data/lib/markdown_composer/composition_buffer.rb +378 -0
- data/lib/markdown_composer/data_path.rb +313 -0
- data/lib/markdown_composer/diagnostics.rb +63 -0
- data/lib/markdown_composer/document_index/html_parser.rb +84 -0
- data/lib/markdown_composer/document_index/markdown_parser.rb +338 -0
- data/lib/markdown_composer/document_index.rb +94 -0
- data/lib/markdown_composer/executor.rb +284 -0
- data/lib/markdown_composer/markdown_renderer.rb +105 -0
- data/lib/markdown_composer/plan.rb +436 -0
- data/lib/markdown_composer/plan_builder.rb +111 -0
- data/lib/markdown_composer/registries/action_entries.rb +26 -0
- data/lib/markdown_composer/registries/condition_entries.rb +58 -0
- data/lib/markdown_composer/registries/registry.rb +69 -0
- data/lib/markdown_composer/registries/source_entries.rb +18 -0
- data/lib/markdown_composer/registries/support_values.rb +23 -0
- data/lib/markdown_composer/registries/take_entries.rb +31 -0
- data/lib/markdown_composer/registries/take_registry.rb +18 -0
- data/lib/markdown_composer/registries/target_entries.rb +40 -0
- data/lib/markdown_composer/registries/unit_token_entries.rb +62 -0
- data/lib/markdown_composer/registries/where_registry.rb +84 -0
- data/lib/markdown_composer/registries.rb +46 -0
- data/lib/markdown_composer/result.rb +34 -0
- data/lib/markdown_composer/selection_resolver.rb +181 -0
- data/lib/markdown_composer/source.rb +57 -0
- data/lib/markdown_composer/source_list_builder.rb +47 -0
- data/lib/markdown_composer/take.rb +129 -0
- data/lib/markdown_composer/transform_options.rb +66 -0
- data/lib/markdown_composer/transform_runner/content_placement.rb +63 -0
- data/lib/markdown_composer/transform_runner/field_interpolator.rb +213 -0
- data/lib/markdown_composer/transform_runner/heading_numbering.rb +106 -0
- data/lib/markdown_composer/transform_runner/scope_resolver.rb +87 -0
- data/lib/markdown_composer/transform_runner.rb +264 -0
- data/lib/markdown_composer/transforms/default_entries.rb +31 -0
- data/lib/markdown_composer/transforms/registry.rb +11 -0
- data/lib/markdown_composer/validator.rb +378 -0
- data/lib/markdown_composer/value_object.rb +15 -0
- data/lib/markdown_composer/version.rb +5 -0
- data/lib/markdown_composer/where.rb +313 -0
- data/lib/markdown_composer.rb +114 -0
- metadata +260 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Markdown Composer Take Modifiers
|
|
3
|
+
type: reference
|
|
4
|
+
status: current
|
|
5
|
+
updated: 2026-06-01
|
|
6
|
+
description: Public reference for Markdown Composer take syntax, operators, ordering, ranges, percentages, random selection, and field-take usage.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Markdown Composer Take Modifiers
|
|
10
|
+
|
|
11
|
+
### References
|
|
12
|
+
|
|
13
|
+
| What | Where |
|
|
14
|
+
|------|-------|
|
|
15
|
+
| Unit tokens | [./md_composer_unit_tokens.md](./md_composer_unit_tokens.md) |
|
|
16
|
+
| Where conditions | [./md_composer_where.md](./md_composer_where.md) |
|
|
17
|
+
| Select syntax | [../compose/md_composer_compose_select.md](../compose/md_composer_compose_select.md) |
|
|
18
|
+
| Include syntax | [../compose/md_composer_compose_include.md](../compose/md_composer_compose_include.md) |
|
|
19
|
+
| Target syntax | [../compose/md_composer_compose_targets.md](../compose/md_composer_compose_targets.md) |
|
|
20
|
+
| Transform scope | [../transform/md_composer_transform_scope.md](../transform/md_composer_transform_scope.md) |
|
|
21
|
+
| Diagnostics | [./md_composer_reference_diagnostics.md](./md_composer_reference_diagnostics.md) |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. Purpose
|
|
26
|
+
|
|
27
|
+
`take` narrows an ordered set of matched units.
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
{ "select" => "heading_2_section[first:2]" }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Take is shared selector syntax. You can use it anywhere selector-like fields accept a unit token:
|
|
34
|
+
|
|
35
|
+
| Field | Example |
|
|
36
|
+
|-------|---------|
|
|
37
|
+
| Compose `select` | `heading_2_section[position:2,3]` |
|
|
38
|
+
| Compose `include` | `paragraph[first:1]` |
|
|
39
|
+
| Compose `target` | `heading_2_section[last:1]` |
|
|
40
|
+
| Transform `scope` | `paragraph[odd]` |
|
|
41
|
+
|
|
42
|
+
## 2. Syntax Anatomy
|
|
43
|
+
|
|
44
|
+
Take lives inside square brackets immediately after the unit token.
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
selector = unit_token [take] [where condition]
|
|
48
|
+
|
|
49
|
+
take = [operator]
|
|
50
|
+
| [operator:value]
|
|
51
|
+
| [operator:value,value]
|
|
52
|
+
| [operator:range]
|
|
53
|
+
|
|
54
|
+
range = n..n | n..last
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
paragraph[first:2]
|
|
61
|
+
heading_2_section[position:2,3]
|
|
62
|
+
heading_2_section[range:2..4]
|
|
63
|
+
list_item[odd]
|
|
64
|
+
table_row[except:1]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Write order and match order:
|
|
68
|
+
|
|
69
|
+
| Step | Meaning |
|
|
70
|
+
|------|---------|
|
|
71
|
+
| Write order | `unit_token[take] where condition` |
|
|
72
|
+
| Match order | Match unit token, filter with `where`, then apply take to the filtered results. |
|
|
73
|
+
|
|
74
|
+
Positions are 1-based. Position `0` is invalid. Negative positions count from the end in structured take values.
|
|
75
|
+
|
|
76
|
+
## 3. Operator Table
|
|
77
|
+
|
|
78
|
+
This table is generated from the current public capabilities contract.
|
|
79
|
+
|
|
80
|
+
| Operator | Alias | Value | Support | Meaning |
|
|
81
|
+
|----------|-------|-------|---------|---------|
|
|
82
|
+
| `all` | | none | normal | Keep all matches. |
|
|
83
|
+
| `first` | | positive integer | normal | Keep the first N matches. |
|
|
84
|
+
| `last` | | positive integer | normal | Keep the last N matches. |
|
|
85
|
+
| `position` | | integer list | normal | Keep one or more 1-based positions. |
|
|
86
|
+
| `ranges` | `range` | range list | normal | Keep one or more inclusive ranges. |
|
|
87
|
+
| `skip` | | positive integer | normal | Ignore the first N matches. |
|
|
88
|
+
| `skip_last` | | positive integer | normal | Ignore the last N matches. |
|
|
89
|
+
| `every` | | positive integer | normal | Keep every Nth match. |
|
|
90
|
+
| `odd` | | none | normal | Keep odd 1-based positions. |
|
|
91
|
+
| `even` | | none | normal | Keep even 1-based positions. |
|
|
92
|
+
| `except` | | integer list | normal | Keep all except selected positions. |
|
|
93
|
+
| `top_percent` | | percent | advanced | Keep a percentage from the start. |
|
|
94
|
+
| `bottom_percent` | | percent | advanced | Keep a percentage from the end. |
|
|
95
|
+
| `middle` | | positive integer | advanced | Keep N matches from the middle. |
|
|
96
|
+
| `middle_percent` | | percent | advanced | Keep a percentage from the middle. |
|
|
97
|
+
| `alternate` | | positive integer | advanced | Keep alternating groups of matches. |
|
|
98
|
+
| `random` | | positive integer | advanced | Keep seeded random matches. |
|
|
99
|
+
|
|
100
|
+
## 4. Common Take Forms
|
|
101
|
+
|
|
102
|
+
| Need | Syntax |
|
|
103
|
+
|------|--------|
|
|
104
|
+
| First match | `paragraph[first:1]` |
|
|
105
|
+
| First three matches | `paragraph[first:3]` |
|
|
106
|
+
| Last match | `heading_2_section[last:1]` |
|
|
107
|
+
| Positions two and three | `heading_2_section[position:2,3]` |
|
|
108
|
+
| Range two through four | `heading_2_section[range:2..4]` |
|
|
109
|
+
| Every second item | `list_item[every:2]` |
|
|
110
|
+
| Odd items | `list_item[odd]` |
|
|
111
|
+
| Even items | `list_item[even]` |
|
|
112
|
+
| All except first and third | `paragraph[except:1,3]` |
|
|
113
|
+
| Skip the first match | `paragraph[skip:1]` |
|
|
114
|
+
| Skip the last match | `paragraph[skip_last:1]` |
|
|
115
|
+
|
|
116
|
+
## 5. Ranges And Positions
|
|
117
|
+
|
|
118
|
+
Use `position` for a list of exact positions:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
{ "select" => "heading_2_section[position:1,3]" }
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Use `range` or canonical `ranges` for inclusive spans:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
{ "select" => "heading_2_section[range:2..4]" }
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Use `last` in ranges when the end should be the final match:
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
{ "select" => "heading_2_section[range:2..last]" }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Multiple selected indexes are de-duplicated and returned in source order.
|
|
137
|
+
|
|
138
|
+
## 6. Advanced Take Forms
|
|
139
|
+
|
|
140
|
+
Advanced operators are exposed by capabilities, but they should be presented carefully in public authoring UIs.
|
|
141
|
+
|
|
142
|
+
| Need | Syntax |
|
|
143
|
+
|------|--------|
|
|
144
|
+
| First quarter of matches | `paragraph[top_percent:25]` |
|
|
145
|
+
| Last quarter of matches | `paragraph[bottom_percent:25]` |
|
|
146
|
+
| Middle three matches | `paragraph[middle:3]` |
|
|
147
|
+
| Middle half of matches | `paragraph[middle_percent:50]` |
|
|
148
|
+
| Alternating groups of two | `list_item[alternate:2]` |
|
|
149
|
+
| Seeded random sample of three | `paragraph[random:3]` |
|
|
150
|
+
|
|
151
|
+
`random` is deterministic when the selection runner is given a seed. Validate random-selection plans before relying on them in reproducible docs or tests.
|
|
152
|
+
|
|
153
|
+
## 7. Include Take
|
|
154
|
+
|
|
155
|
+
In `include`, take runs inside each selected unit.
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
{
|
|
159
|
+
"select" => "heading_2_section[position:1,2]",
|
|
160
|
+
"include" => "paragraph[first:1]"
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
This keeps the first paragraph from each selected section. It does not keep the first paragraph across the whole source document.
|
|
165
|
+
|
|
166
|
+
## 8. Target And Scope Take
|
|
167
|
+
|
|
168
|
+
In `target`, take matches against the current composition buffer:
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
{
|
|
172
|
+
"action" => "replace",
|
|
173
|
+
"target" => "paragraph[first:1] where text:contains(\"TODO\")"
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
In transform `scope`, take matches against the composed output or row-level fragment:
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
{
|
|
181
|
+
"scope" => "heading_2_section[last:1]",
|
|
182
|
+
"transform" => "replace_text",
|
|
183
|
+
"mode" => "literal",
|
|
184
|
+
"options" => "from:Draft; to:Final"
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## 9. Field-Take In Where
|
|
189
|
+
|
|
190
|
+
Where conditions have a separate field-take form for text fields:
|
|
191
|
+
|
|
192
|
+
```text
|
|
193
|
+
source_text[first:1]:contains("::-")
|
|
194
|
+
text[first:5]:contains("Warning")
|
|
195
|
+
title[first:2]:equals("API")
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
This is not the same as selector take. Selector take chooses matched units; field-take narrows words inside a field value before comparing.
|
|
199
|
+
|
|
200
|
+
## 10. Diagnostics
|
|
201
|
+
|
|
202
|
+
| Situation | Diagnostic family |
|
|
203
|
+
|-----------|-------------------|
|
|
204
|
+
| Unknown take operator | `take.invalid` |
|
|
205
|
+
| Positive count operator is zero or negative | `take.invalid` |
|
|
206
|
+
| `position` or `except` includes zero | `take.invalid` |
|
|
207
|
+
| Range missing `from` or `to` | `take.invalid` |
|
|
208
|
+
| Range includes zero | `take.invalid` |
|
|
209
|
+
| Unsupported field-take in `where` | `where.field_take_unsupported` |
|
|
210
|
+
| Invalid field-take in `where` | `where.field_take_invalid` |
|
|
211
|
+
|
|
212
|
+
## 11. Common Mistakes
|
|
213
|
+
|
|
214
|
+
| Mistake | Fix |
|
|
215
|
+
|---------|-----|
|
|
216
|
+
| Calling `take` an action. | Put take inside `select`, `include`, `target`, or `scope` selectors. |
|
|
217
|
+
| Assuming include take runs globally. | Include take runs inside each selected unit. |
|
|
218
|
+
| Using position zero. | Use 1-based positions. |
|
|
219
|
+
| Expecting target take to search source documents. | Target take searches the current buffer. |
|
|
220
|
+
| Expecting scope take to search source documents. | Scope take searches composed output or a row-level fragment. |
|
|
221
|
+
| Mixing selector take and field-take. | Use selector take for units; use field-take inside `where` text fields. |
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Markdown Composer Unit Tokens
|
|
3
|
+
type: reference
|
|
4
|
+
status: current
|
|
5
|
+
updated: 2026-06-01
|
|
6
|
+
description: Public reference for every Markdown Composer unit token, alias, and supported use context.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Markdown Composer Unit Tokens
|
|
10
|
+
|
|
11
|
+
### References
|
|
12
|
+
|
|
13
|
+
| What | Where |
|
|
14
|
+
|------|-------|
|
|
15
|
+
| Select syntax | [../compose/md_composer_compose_select.md](../compose/md_composer_compose_select.md) |
|
|
16
|
+
| Include syntax | [../compose/md_composer_compose_include.md](../compose/md_composer_compose_include.md) |
|
|
17
|
+
| Target syntax | [../compose/md_composer_compose_targets.md](../compose/md_composer_compose_targets.md) |
|
|
18
|
+
| Transform scope | [../transform/md_composer_transform_scope.md](../transform/md_composer_transform_scope.md) |
|
|
19
|
+
| Registries | [./md_composer_reference_registries.md](./md_composer_reference_registries.md) |
|
|
20
|
+
| Capabilities | [./md_composer_reference_capabilities.md](./md_composer_reference_capabilities.md) |
|
|
21
|
+
| Take modifiers | [./md_composer_take.md](./md_composer_take.md) |
|
|
22
|
+
| Where conditions | [./md_composer_where.md](./md_composer_where.md) |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 1. Purpose
|
|
27
|
+
|
|
28
|
+
Unit tokens name the Markdown Composer units that can be selected, included, targeted, or transformed.
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
{ "select" => "heading_2_section[first:1]" }
|
|
32
|
+
{ "include" => "heading_title, paragraph[first:1]" }
|
|
33
|
+
{ "target" => "paragraph where text:contains(\"TODO\")" }
|
|
34
|
+
{ "scope" => "heading_2_section where title:equals(\"API\")" }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Use canonical tokens in saved plans. Aliases are accepted for authoring convenience and normalize to canonical tokens.
|
|
38
|
+
|
|
39
|
+
## 2. Syntax Anatomy
|
|
40
|
+
|
|
41
|
+
Unit tokens appear inside selector-like fields.
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
selector = unit_token [take] [where condition]
|
|
45
|
+
|
|
46
|
+
include_item = unit_token [take] [where condition]
|
|
47
|
+
|
|
48
|
+
target_selector = unit_token [take] [where condition]
|
|
49
|
+
|
|
50
|
+
scope_selector = unit_token [take] [where condition]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
For bracketed take syntax, see [Take Modifiers](./md_composer_take.md).
|
|
54
|
+
For `where` field, predicate, and group syntax, see [Where Conditions](./md_composer_where.md).
|
|
55
|
+
|
|
56
|
+
Context support matters:
|
|
57
|
+
|
|
58
|
+
| Support value | Meaning |
|
|
59
|
+
|---------------|---------|
|
|
60
|
+
| `normal` | Supported for ordinary use in that context. |
|
|
61
|
+
| `include_only` | Only valid as an include item. |
|
|
62
|
+
| `output_only` | Only meaningful against composed/structured output. |
|
|
63
|
+
| `advanced` | Exposed as an advanced token; validate against the exact plan behavior. |
|
|
64
|
+
| `adapter_policy` | Requires host policy. |
|
|
65
|
+
| `disabled` | Do not use in that context. |
|
|
66
|
+
|
|
67
|
+
## 3. Full Token List
|
|
68
|
+
|
|
69
|
+
This table is generated from the current public capabilities contract.
|
|
70
|
+
|
|
71
|
+
| Token | Alias | Select | Include | Target | Scope | Meaning |
|
|
72
|
+
|-------|-------|--------|---------|--------|-------|---------|
|
|
73
|
+
| `all` | | normal | normal | disabled | disabled | Whole available content. |
|
|
74
|
+
| `output` | | disabled | disabled | normal | output_only | Whole composed output. |
|
|
75
|
+
| `content` | | disabled | include_only | disabled | advanced | Content inside the selected unit. |
|
|
76
|
+
| `heading_title` | `title` | disabled | include_only | advanced | advanced | Section heading title. |
|
|
77
|
+
| `section` | `sections` | normal | normal | normal | normal | Heading-defined section at any level. |
|
|
78
|
+
| `heading` | `headings` | normal | normal | normal | normal | Heading node at any level. |
|
|
79
|
+
| `paragraph` | `p` | normal | normal | normal | normal | Paragraph node. |
|
|
80
|
+
| `link` | `a` | normal | normal | normal | normal | Link node. |
|
|
81
|
+
| `image` | `img` | normal | normal | normal | normal | Image node. |
|
|
82
|
+
| `list` | | normal | normal | normal | normal | Any list node. |
|
|
83
|
+
| `unordered_list` | `ul` | normal | normal | normal | normal | Bullet list. |
|
|
84
|
+
| `ordered_list` | `ol` | normal | normal | normal | normal | Numbered list. |
|
|
85
|
+
| `list_item` | `li` | normal | normal | normal | normal | List item. |
|
|
86
|
+
| `blockquote` | | normal | normal | normal | normal | Blockquote. |
|
|
87
|
+
| `table` | | normal | normal | normal | normal | Whole table. |
|
|
88
|
+
| `table_head` | `thead` | advanced | advanced | advanced | advanced | Table header group. |
|
|
89
|
+
| `table_body` | `tbody` | advanced | advanced | advanced | advanced | Table body group. |
|
|
90
|
+
| `table_row` | `tr` | advanced | advanced | advanced | advanced | Table row. |
|
|
91
|
+
| `table_header` | `th` | advanced | advanced | advanced | advanced | Header cell. |
|
|
92
|
+
| `table_cell` | `td` | advanced | advanced | advanced | advanced | Table cell. |
|
|
93
|
+
| `code_block` | | normal | normal | normal | normal | Fenced or block code. |
|
|
94
|
+
| `inline_code` | | normal | normal | normal | normal | Inline code span. |
|
|
95
|
+
| `data_block` | | normal | normal | advanced | advanced | Structured YAML/JSON block. |
|
|
96
|
+
| `data_path` | | disabled | include_only | disabled | advanced | Structured data path extraction. |
|
|
97
|
+
| `data_record` | | disabled | disabled | advanced | output_only | Derived structured data record. |
|
|
98
|
+
| `data_value` | | disabled | disabled | advanced | output_only | Derived structured data value. |
|
|
99
|
+
| `mermaid` | | normal | normal | normal | normal | Mermaid diagram block. |
|
|
100
|
+
| `math_block` | | normal | normal | normal | normal | Display math block. |
|
|
101
|
+
| `inline_math` | | normal | normal | normal | normal | Inline math span. |
|
|
102
|
+
| `comment` | | normal | normal | normal | normal | Comment node. |
|
|
103
|
+
| `raw_html` | | adapter_policy | adapter_policy | adapter_policy | adapter_policy | Raw HTML node. |
|
|
104
|
+
| `heading_1_section` | `h1_section` | normal | normal | normal | normal | H1 section. |
|
|
105
|
+
| `heading_1` | `h1` | normal | normal | normal | normal | H1 heading node. |
|
|
106
|
+
| `heading_2_section` | `h2_section` | normal | normal | normal | normal | H2 section. |
|
|
107
|
+
| `heading_2` | `h2` | normal | normal | normal | normal | H2 heading node. |
|
|
108
|
+
| `heading_3_section` | `h3_section` | normal | normal | normal | normal | H3 section. |
|
|
109
|
+
| `heading_3` | `h3` | normal | normal | normal | normal | H3 heading node. |
|
|
110
|
+
| `heading_4_section` | `h4_section` | normal | normal | normal | normal | H4 section. |
|
|
111
|
+
| `heading_4` | `h4` | normal | normal | normal | normal | H4 heading node. |
|
|
112
|
+
| `heading_5_section` | `h5_section` | normal | normal | normal | normal | H5 section. |
|
|
113
|
+
| `heading_5` | `h5` | normal | normal | normal | normal | H5 heading node. |
|
|
114
|
+
| `heading_6_section` | `h6_section` | normal | normal | normal | normal | H6 section. |
|
|
115
|
+
| `heading_6` | `h6` | normal | normal | normal | normal | H6 heading node. |
|
|
116
|
+
|
|
117
|
+
## 4. Section And Heading Tokens
|
|
118
|
+
|
|
119
|
+
Use section tokens when you want a heading and its body.
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
{ "select" => "heading_2_section where title:equals(\"Install\")" }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Use heading tokens when you only want the heading node.
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
{ "select" => "heading_2 where text:contains(\"Install\")" }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Generic section and heading tokens are useful when the exact heading level does not matter:
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
{ "select" => "section where title:contains(\"API\")" }
|
|
135
|
+
{ "scope" => "heading" }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## 5. Include-Only Tokens
|
|
139
|
+
|
|
140
|
+
`content`, `heading_title`, and `data_path` are include-oriented tokens.
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
{
|
|
144
|
+
"select" => "heading_2_section[first:1]",
|
|
145
|
+
"include" => "heading_title, paragraph[first:1]"
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`content` keeps the body content inside the selected unit:
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
{
|
|
153
|
+
"select" => "heading_2_section where title:equals(\"Intro\")",
|
|
154
|
+
"include" => "content"
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`data_path(...)` extracts values from selected structured data blocks:
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
{
|
|
162
|
+
"select" => "data_block where format:equals(\"yaml\")",
|
|
163
|
+
"include" => "data_path(\"metadata.title\")"
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## 6. Structured Data Tokens
|
|
168
|
+
|
|
169
|
+
`data_block` is the normal token for selecting YAML or JSON blocks.
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
{ "select" => "data_block where format:equals(\"json\")" }
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`data_record` and `data_value` are derived node types produced by `data_path(...)`.
|
|
176
|
+
|
|
177
|
+
In standalone Markdown output, extracted data is serialized back to Markdown text, and later target/scope matching reparses that Markdown. Treat `data_record` and `data_value` as structured-output metadata unless a host adapter preserves them as matchable buffer nodes.
|
|
178
|
+
|
|
179
|
+
## 7. Table Tokens
|
|
180
|
+
|
|
181
|
+
`table` is the practical token for most table work.
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
{ "select" => "table[first:1]" }
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Table substructure tokens are available as advanced tokens:
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
{ "select" => "table_row[position:2]" }
|
|
191
|
+
{ "include" => "table_cell" }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Validate table substructure plans carefully because Markdown table parsing can depend on the source shape.
|
|
195
|
+
|
|
196
|
+
## 8. Policy-Gated Tokens
|
|
197
|
+
|
|
198
|
+
`raw_html` requires host policy.
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
{ "select" => "raw_html" }
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Do not expose `raw_html` as an ordinary public control unless the host explicitly enables adapter-policy tokens and accepts the security boundary.
|
|
205
|
+
|
|
206
|
+
## 9. Common Recipes
|
|
207
|
+
|
|
208
|
+
| Need | Token syntax |
|
|
209
|
+
|------|--------------|
|
|
210
|
+
| First H2 section | `heading_2_section[first:1]` |
|
|
211
|
+
| H2 section by title | `heading_2_section where title:equals("API")` |
|
|
212
|
+
| Paragraphs containing text | `paragraph where text:contains("warning")` |
|
|
213
|
+
| First Ruby code block | `code_block[first:1] where language:equals("ruby")` |
|
|
214
|
+
| Any image | `image` |
|
|
215
|
+
| List items two and three | `list_item[position:2,3]` |
|
|
216
|
+
| Structured data block | `data_block where format:equals("yaml")` |
|
|
217
|
+
| Whole table | `table` |
|
|
218
|
+
| All headings | `heading` |
|
|
219
|
+
|
|
220
|
+
## 10. Common Mistakes
|
|
221
|
+
|
|
222
|
+
| Mistake | Fix |
|
|
223
|
+
|---------|-----|
|
|
224
|
+
| Using `heading_2` when you need the section body. | Use `heading_2_section`. |
|
|
225
|
+
| Using `heading_title` as a top-level `select`. | Select a section, then include `heading_title`. |
|
|
226
|
+
| Using `data_path` outside `include`. | Select `data_block`, then use `include: "data_path(...)"`. |
|
|
227
|
+
| Treating `data_value` as a normal post-compose selector. | Target serialized Markdown text or use a host that preserves structured nodes. |
|
|
228
|
+
| Offering `raw_html` in a public UI without policy. | Gate it behind explicit host policy. |
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Markdown Composer Where Conditions
|
|
3
|
+
type: reference
|
|
4
|
+
status: current
|
|
5
|
+
updated: 2026-06-01
|
|
6
|
+
description: Public reference for Markdown Composer where syntax, fields, predicates, groups, and shared selector usage.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Markdown Composer Where Conditions
|
|
10
|
+
|
|
11
|
+
### References
|
|
12
|
+
|
|
13
|
+
| What | Where |
|
|
14
|
+
|------|-------|
|
|
15
|
+
| Unit tokens | [./md_composer_unit_tokens.md](./md_composer_unit_tokens.md) |
|
|
16
|
+
| Take modifiers | [./md_composer_take.md](./md_composer_take.md) |
|
|
17
|
+
| Select syntax | [../compose/md_composer_compose_select.md](../compose/md_composer_compose_select.md) |
|
|
18
|
+
| Include syntax | [../compose/md_composer_compose_include.md](../compose/md_composer_compose_include.md) |
|
|
19
|
+
| Target syntax | [../compose/md_composer_compose_targets.md](../compose/md_composer_compose_targets.md) |
|
|
20
|
+
| Transform scope | [../transform/md_composer_transform_scope.md](../transform/md_composer_transform_scope.md) |
|
|
21
|
+
| Diagnostics | [./md_composer_reference_diagnostics.md](./md_composer_reference_diagnostics.md) |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. Purpose
|
|
26
|
+
|
|
27
|
+
`where` filters matched units by field predicates.
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
{ "select" => "heading_2_section where title:contains(\"API\")" }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`where` is shared selector syntax. You can use it anywhere a selector-like field accepts it:
|
|
34
|
+
|
|
35
|
+
| Field | Example |
|
|
36
|
+
|-------|---------|
|
|
37
|
+
| Compose `select` | `heading_2_section where title:equals("Install")` |
|
|
38
|
+
| Compose `include` | `paragraph where text:contains("warning")` |
|
|
39
|
+
| Compose `target` | `paragraph where text:contains("TODO")` |
|
|
40
|
+
| Transform `scope` | `code_block where language:equals("ruby")` |
|
|
41
|
+
|
|
42
|
+
## 2. Syntax Anatomy
|
|
43
|
+
|
|
44
|
+
Write a `where` condition after the unit token and bracketed take modifier.
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
selector = unit_token [take] where condition
|
|
48
|
+
|
|
49
|
+
condition = field:predicate(value)
|
|
50
|
+
| field:exists
|
|
51
|
+
| field:equals(value)
|
|
52
|
+
| field:value
|
|
53
|
+
| group(condition; condition; ...)
|
|
54
|
+
|
|
55
|
+
group = all | any | none | not | xor
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For selector-level take syntax, see [Take Modifiers](./md_composer_take.md).
|
|
59
|
+
|
|
60
|
+
Common compact forms:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
title:contains("API")
|
|
64
|
+
text:equals("Draft")
|
|
65
|
+
links:exists
|
|
66
|
+
empty:false
|
|
67
|
+
position:range(2..4)
|
|
68
|
+
any(title:contains("API"); title:contains("CLI"))
|
|
69
|
+
not(empty:true)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Syntax order and execution order are slightly different:
|
|
73
|
+
|
|
74
|
+
| Step | Meaning |
|
|
75
|
+
|------|---------|
|
|
76
|
+
| Write order | `unit_token[take] where condition` |
|
|
77
|
+
| Match order | Match unit token, filter with `where`, then apply take to the filtered results. |
|
|
78
|
+
|
|
79
|
+
## 3. Fields
|
|
80
|
+
|
|
81
|
+
| Field | Predicates | Use for |
|
|
82
|
+
|-------|------------|---------|
|
|
83
|
+
| `title` | `contains`, `starts_with`, `ends_with`, `equals`, `matches` | Heading or section titles. |
|
|
84
|
+
| `text` | `contains`, `starts_with`, `ends_with`, `equals`, `matches` | Visible text content. |
|
|
85
|
+
| `source_text` | `contains`, `starts_with`, `ends_with`, `equals`, `matches` | Retained source representation. |
|
|
86
|
+
| `position` | `equals`, `range` | 1-based position among matched units. |
|
|
87
|
+
| `language` | `equals` | Fenced code language. |
|
|
88
|
+
| `diagram_type` | `equals` | Normalized Mermaid diagram type. |
|
|
89
|
+
| `format` | `equals` | Structured data format, such as YAML or JSON. |
|
|
90
|
+
| `location` | `equals` | Structured data location metadata. |
|
|
91
|
+
| `links` | `exists` | Units containing links. |
|
|
92
|
+
| `images` | `exists` | Units containing images. |
|
|
93
|
+
| `code` | `exists` | Units containing code. |
|
|
94
|
+
| `numbers` | `exists` | Text containing digits. |
|
|
95
|
+
| `empty` | `equals` | Empty or non-empty units. |
|
|
96
|
+
| `length` | `min`, `max`, `range` | Character length. |
|
|
97
|
+
| `word_count` | `min`, `max`, `range` | Word count. |
|
|
98
|
+
| `child` | `exists` | Sections with nested child sections. |
|
|
99
|
+
|
|
100
|
+
Capability metadata currently exposes some where fields and groups with `enabled: false` even though the condition map documents usable predicates. GUI builders should consult `capabilities["where"]["condition_map"]` as the practical field-to-predicate map.
|
|
101
|
+
|
|
102
|
+
## 4. Predicates
|
|
103
|
+
|
|
104
|
+
| Predicate | Value | Example |
|
|
105
|
+
|-----------|-------|---------|
|
|
106
|
+
| `contains` | text | `text:contains("warning")` |
|
|
107
|
+
| `starts_with` | text | `title:starts_with("API")` |
|
|
108
|
+
| `ends_with` | text | `title:ends_with("Reference")` |
|
|
109
|
+
| `equals` | scalar | `language:equals("ruby")` |
|
|
110
|
+
| `range` | range | `position:range(2..4)` |
|
|
111
|
+
| `exists` | none | `links:exists` |
|
|
112
|
+
| `min` | number | `word_count:min(20)` |
|
|
113
|
+
| `max` | number | `length:max(280)` |
|
|
114
|
+
| `matches` | regex | `text:matches("^TODO")` |
|
|
115
|
+
|
|
116
|
+
`matches` is advanced regex matching. Validate regex conditions before exposing them in a public authoring UI.
|
|
117
|
+
|
|
118
|
+
## 5. Groups
|
|
119
|
+
|
|
120
|
+
Groups combine conditions. Child conditions are separated with top-level semicolons.
|
|
121
|
+
|
|
122
|
+
Group tokens are `all`, `any`, `none`, `not`, and `xor`.
|
|
123
|
+
|
|
124
|
+
| Group | Meaning | Example |
|
|
125
|
+
|-------|---------|---------|
|
|
126
|
+
| `all(...)` | Every child condition must match. | `all(title:contains("API"); links:exists)` |
|
|
127
|
+
| `any(...)` | At least one child condition must match. | `any(title:contains("API"); title:contains("CLI"))` |
|
|
128
|
+
| `none(...)` | No child condition may match. | `none(text:contains("Deprecated"); links:exists)` |
|
|
129
|
+
| `not(...)` | Negates one condition or group. | `not(empty:true)` |
|
|
130
|
+
| `xor(...)` | Exactly one child condition must match. | `xor(links:exists; images:exists)` |
|
|
131
|
+
|
|
132
|
+
Use semicolons inside groups, not commas:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
any(title:contains("API"); title:contains("CLI"))
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## 6. Field-Take
|
|
139
|
+
|
|
140
|
+
Text fields can apply bracketed field-take before the predicate compares values.
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
source_text[first:1]:contains("::-")
|
|
144
|
+
title[first:2]:contains("API")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Field-take is supported for:
|
|
148
|
+
|
|
149
|
+
| Field | Example |
|
|
150
|
+
|-------|---------|
|
|
151
|
+
| `title` | `title[first:1]:equals("Install")` |
|
|
152
|
+
| `text` | `text[first:5]:contains("Warning")` |
|
|
153
|
+
| `source_text` | `source_text[first:1]:contains("::-")` |
|
|
154
|
+
|
|
155
|
+
Use field-take when you need to test part of a text field rather than the whole field.
|
|
156
|
+
|
|
157
|
+
## 7. Shared Field Examples
|
|
158
|
+
|
|
159
|
+
### 7.1. Select
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
{
|
|
163
|
+
"select" => "heading_2_section where title:contains(\"API\")",
|
|
164
|
+
"action" => "set"
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 7.2. Include
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
{
|
|
172
|
+
"select" => "heading_2_section",
|
|
173
|
+
"include" => "paragraph where text:contains(\"warning\")"
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The include condition runs inside each selected section.
|
|
178
|
+
|
|
179
|
+
### 7.3. Target
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
{
|
|
183
|
+
"select" => "paragraph where text:contains(\"New note\")",
|
|
184
|
+
"action" => "replace",
|
|
185
|
+
"target" => "paragraph where text:contains(\"Old note\")"
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The target condition matches the current composition buffer.
|
|
190
|
+
|
|
191
|
+
### 7.4. Transform Scope
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
{
|
|
195
|
+
"scope" => "code_block where language:equals(\"ruby\")",
|
|
196
|
+
"transform" => "replace_text",
|
|
197
|
+
"mode" => "literal",
|
|
198
|
+
"options" => "from:TODO; to:DONE"
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The scope condition matches the composed output or row-level fragment, not the original source document.
|
|
203
|
+
|
|
204
|
+
## 8. Diagnostics
|
|
205
|
+
|
|
206
|
+
| Situation | Diagnostic family |
|
|
207
|
+
|-----------|-------------------|
|
|
208
|
+
| Unknown field | `where.field_unknown` |
|
|
209
|
+
| Unknown or unsupported predicate | `where.operator_unknown` |
|
|
210
|
+
| Malformed predicate | `where.predicate_malformed` |
|
|
211
|
+
| Invalid regex | `where.regex_invalid` |
|
|
212
|
+
| Unsupported field-take | `where.field_take_unsupported` |
|
|
213
|
+
| Invalid field-take | `where.field_take_invalid` |
|
|
214
|
+
| Position zero | `where.position_zero` |
|
|
215
|
+
|
|
216
|
+
See [Diagnostics](./md_composer_reference_diagnostics.md) for the broader validation model.
|
|
217
|
+
|
|
218
|
+
## 9. Common Mistakes
|
|
219
|
+
|
|
220
|
+
| Mistake | Fix |
|
|
221
|
+
|---------|-----|
|
|
222
|
+
| Treating `where` as select-only syntax. | Use it anywhere selector syntax is accepted: select, include, target, and scope. |
|
|
223
|
+
| Writing commas inside groups. | Use semicolons between child conditions. |
|
|
224
|
+
| Putting `where` before the unit token. | Use `unit_token[take] where condition`. |
|
|
225
|
+
| Expecting target `where` to search the original source. | Target conditions search the current composition buffer. |
|
|
226
|
+
| Expecting transform scope `where` to search source documents. | Scope conditions search composed output or a row-level fragment. |
|
|
227
|
+
| Using regex without validation. | Validate `matches(...)` expressions and expose them as advanced controls. |
|