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.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +278 -0
  5. data/ROADMAP.md +80 -0
  6. data/docs/_md_composer_architecture.md +50 -0
  7. data/docs/_md_composer_cheatsheet.md +72 -0
  8. data/docs/_md_composer_concepts.md +64 -0
  9. data/docs/_md_composer_dev_guide.md +55 -0
  10. data/docs/_md_composer_getting_started.md +114 -0
  11. data/docs/_md_composer_readme.md +93 -0
  12. data/docs/_md_composer_user_guide.md +65 -0
  13. data/docs/ai/md_composer_ai_audit.md +35 -0
  14. data/docs/ai/md_composer_ai_canonical_docs.md +44 -0
  15. data/docs/ai/md_composer_ai_source_map.md +39 -0
  16. data/docs/compose/md_composer_compose_actions.md +338 -0
  17. data/docs/compose/md_composer_compose_anatomy.md +156 -0
  18. data/docs/compose/md_composer_compose_buffer.md +81 -0
  19. data/docs/compose/md_composer_compose_examples.md +31 -0
  20. data/docs/compose/md_composer_compose_include.md +136 -0
  21. data/docs/compose/md_composer_compose_select.md +198 -0
  22. data/docs/compose/md_composer_compose_sources.md +161 -0
  23. data/docs/compose/md_composer_compose_targets.md +194 -0
  24. data/docs/examples/md_composer_example_basic_compose.md +57 -0
  25. data/docs/examples/md_composer_example_buffer_target_actions.md +83 -0
  26. data/docs/examples/md_composer_example_fixtures.md +62 -0
  27. data/docs/examples/md_composer_example_html_output.md +50 -0
  28. data/docs/examples/md_composer_example_modify.md +77 -0
  29. data/docs/examples/md_composer_example_multi_row_compose.md +67 -0
  30. data/docs/examples/md_composer_example_ruby_plans.md +62 -0
  31. data/docs/examples/md_composer_example_structured_data.md +68 -0
  32. data/docs/examples/md_composer_example_transforms.md +68 -0
  33. data/docs/examples/md_composer_example_yaml_json_rows.md +56 -0
  34. data/docs/examples/md_composer_examples_readme.md +45 -0
  35. data/docs/examples/md_composer_runnable_examples.md +374 -0
  36. data/docs/examples/md_composer_source_ruby_dsl.md +88 -0
  37. data/docs/reference/md_composer_nested.md +170 -0
  38. data/docs/reference/md_composer_reference_api.md +71 -0
  39. data/docs/reference/md_composer_reference_capabilities.md +63 -0
  40. data/docs/reference/md_composer_reference_diagnostics.md +54 -0
  41. data/docs/reference/md_composer_reference_plan_schema.md +75 -0
  42. data/docs/reference/md_composer_reference_registries.md +63 -0
  43. data/docs/reference/md_composer_take.md +221 -0
  44. data/docs/reference/md_composer_unit_tokens.md +228 -0
  45. data/docs/reference/md_composer_where.md +227 -0
  46. data/docs/transform/md_composer_transform_anatomy.md +112 -0
  47. data/docs/transform/md_composer_transform_examples.md +30 -0
  48. data/docs/transform/md_composer_transform_modes.md +83 -0
  49. data/docs/transform/md_composer_transform_options.md +142 -0
  50. data/docs/transform/md_composer_transform_scope.md +97 -0
  51. data/docs/transform/md_composer_transform_transforms.md +99 -0
  52. data/examples/README.md +20 -0
  53. data/examples/advanced_composer.rb +207 -0
  54. data/examples/basic_compose.rb +24 -0
  55. data/examples/complex_composer.rb +235 -0
  56. data/examples/example_support.rb +18 -0
  57. data/examples/fixtures/current.md +179 -0
  58. data/examples/fixtures/faq.md +58 -0
  59. data/examples/fixtures/guide.md +62 -0
  60. data/examples/fixtures/site_intro.md +29 -0
  61. data/examples/fixtures/source.html +22 -0
  62. data/examples/html_input.rb +26 -0
  63. data/examples/output/advanced_composer.md +76 -0
  64. data/examples/output/basic_compose.md +25 -0
  65. data/examples/output/complex_composer.md +85 -0
  66. data/examples/output/html_input.md +4 -0
  67. data/examples/output/source_list_dsl.md +126 -0
  68. data/examples/output/standard_composer.md +46 -0
  69. data/examples/output/standard_sources_buffer.md +31 -0
  70. data/examples/output/yaml_plan.md +43 -0
  71. data/examples/plans/basic.yml +20 -0
  72. data/examples/source_list_dsl.rb +41 -0
  73. data/examples/standard_composer.rb +42 -0
  74. data/examples/standard_sources_buffer.rb +62 -0
  75. data/examples/yaml_plan.rb +17 -0
  76. data/lib/markdown_composer/capabilities.rb +223 -0
  77. data/lib/markdown_composer/composition_buffer.rb +378 -0
  78. data/lib/markdown_composer/data_path.rb +313 -0
  79. data/lib/markdown_composer/diagnostics.rb +63 -0
  80. data/lib/markdown_composer/document_index/html_parser.rb +84 -0
  81. data/lib/markdown_composer/document_index/markdown_parser.rb +338 -0
  82. data/lib/markdown_composer/document_index.rb +94 -0
  83. data/lib/markdown_composer/executor.rb +284 -0
  84. data/lib/markdown_composer/markdown_renderer.rb +105 -0
  85. data/lib/markdown_composer/plan.rb +436 -0
  86. data/lib/markdown_composer/plan_builder.rb +111 -0
  87. data/lib/markdown_composer/registries/action_entries.rb +26 -0
  88. data/lib/markdown_composer/registries/condition_entries.rb +58 -0
  89. data/lib/markdown_composer/registries/registry.rb +69 -0
  90. data/lib/markdown_composer/registries/source_entries.rb +18 -0
  91. data/lib/markdown_composer/registries/support_values.rb +23 -0
  92. data/lib/markdown_composer/registries/take_entries.rb +31 -0
  93. data/lib/markdown_composer/registries/take_registry.rb +18 -0
  94. data/lib/markdown_composer/registries/target_entries.rb +40 -0
  95. data/lib/markdown_composer/registries/unit_token_entries.rb +62 -0
  96. data/lib/markdown_composer/registries/where_registry.rb +84 -0
  97. data/lib/markdown_composer/registries.rb +46 -0
  98. data/lib/markdown_composer/result.rb +34 -0
  99. data/lib/markdown_composer/selection_resolver.rb +181 -0
  100. data/lib/markdown_composer/source.rb +57 -0
  101. data/lib/markdown_composer/source_list_builder.rb +47 -0
  102. data/lib/markdown_composer/take.rb +129 -0
  103. data/lib/markdown_composer/transform_options.rb +66 -0
  104. data/lib/markdown_composer/transform_runner/content_placement.rb +63 -0
  105. data/lib/markdown_composer/transform_runner/field_interpolator.rb +213 -0
  106. data/lib/markdown_composer/transform_runner/heading_numbering.rb +106 -0
  107. data/lib/markdown_composer/transform_runner/scope_resolver.rb +87 -0
  108. data/lib/markdown_composer/transform_runner.rb +264 -0
  109. data/lib/markdown_composer/transforms/default_entries.rb +31 -0
  110. data/lib/markdown_composer/transforms/registry.rb +11 -0
  111. data/lib/markdown_composer/validator.rb +378 -0
  112. data/lib/markdown_composer/value_object.rb +15 -0
  113. data/lib/markdown_composer/version.rb +5 -0
  114. data/lib/markdown_composer/where.rb +313 -0
  115. data/lib/markdown_composer.rb +114 -0
  116. 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. |