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
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1efddd31cae13aa57937ff72966d3d45553318b7f6e723153b09f818dc7fb554
|
|
4
|
+
data.tar.gz: 67639e63dc038422d7226bf54b3535aad398049c3bd4f08076af7383fbf192b4
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: daaa025bb16b154e341938a4b357b85ab9dc4e23eba85a22d6f114996a3add3ee0bd855b307b9654a85b37df9e9b9609f17bd104ba56dceb4cf3fa25bc32f1c4
|
|
7
|
+
data.tar.gz: c37dc0643e032a97bd04d7b468941be08bc8d88ae0c348b45e0023f92406ce433a36a6dc7e03cc172fe6f3e27fad970e93862f40657e398a85efc56c0c709d0a
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.7.0 - 2026-05-14
|
|
4
|
+
|
|
5
|
+
- Moved test fixtures into the gem so the public test suite is self-contained.
|
|
6
|
+
- Added `bundle exec rake test` as the standard test entry point.
|
|
7
|
+
- Added GitHub Actions CI for Ruby 3.1, 3.2, 3.3, and 3.4.
|
|
8
|
+
- Updated public gem metadata for the standalone `SocIt2Em/markdown_composer` repository.
|
|
9
|
+
- Raised declared Ruby support to `>= 3.1` so the public support claim matches the CI matrix.
|
|
10
|
+
- Documented the validation boundary between the core gem and host frameworks such as Rails.
|
|
11
|
+
- Documented regex policy expectations for hosts exposing Composer configs to untrusted users.
|
|
12
|
+
- Added focused edge-case tests that raise gem library line coverage above 95%.
|
|
13
|
+
|
|
14
|
+
## 0.6.5.1 - 2026-05-14
|
|
15
|
+
|
|
16
|
+
- Added the standalone `markdown_composer` gem package.
|
|
17
|
+
- Added public APIs for composition, normalization, validation, capabilities, YAML/JSON import/export, row import/export, and the Ruby plan DSL.
|
|
18
|
+
- Added Markdown-first source indexing, selection, include, take, where, action, target, transform, data-path, and diagnostics support.
|
|
19
|
+
- Added HTML output support and best-effort HTML input conversion.
|
|
20
|
+
- Enabled regex matching, regex text replacement, deterministic random take, Mermaid/math/comment tokens, and HTML link attribute transforms.
|
|
21
|
+
- Kept raw HTML, sanitise transforms, host adapter transforms, and target-order behavior behind host policy or roadmap boundaries.
|
|
22
|
+
- Added fixture-backed UAT, release-readiness coverage, and coupling tests for app independence.
|
|
23
|
+
- Kept public capability metadata host-generic so GUI builders do not depend on application-specific document models.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SocIt2Em
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# Markdown Composer
|
|
2
|
+
|
|
3
|
+
Markdown Composer is a headless Ruby gem for selecting, composing, validating, and transforming Markdown-first document fragments.
|
|
4
|
+
|
|
5
|
+
It is framework-independent. It does not depend on Rails, an ORM, an authorization system, a template engine, a component framework, or a UI. Host applications pass plain Markdown/HTML sources into the gem and own source lookup, permissions, sanitization, asset handling, and presentation.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Ruby `>= 3.1`
|
|
10
|
+
- `commonmarker >= 0.23.10, < 3`
|
|
11
|
+
- `nokogiri >= 1.13.10, < 2`
|
|
12
|
+
|
|
13
|
+
Development dependencies are limited to test and benchmark tooling.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
From RubyGems:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
# Gemfile
|
|
21
|
+
gem "markdown_composer"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Then run:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bundle install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
For a standalone Ruby script:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
gem install markdown_composer
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
From a Git repository before a RubyGems release:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
# Gemfile
|
|
40
|
+
gem "markdown_composer", git: "https://github.com/SocIt2Em/markdown_composer.git"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If installing from source before a RubyGems release, point Bundler at the standalone repository:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
# Gemfile
|
|
47
|
+
gem "markdown_composer", git: "https://github.com/SocIt2Em/markdown_composer.git"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
require "markdown_composer"
|
|
54
|
+
|
|
55
|
+
sources = MarkdownComposer.source_list do
|
|
56
|
+
current File.read("source_current.md")
|
|
57
|
+
explicit :guide, File.read("guide.md")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
plan = MarkdownComposer.plan do
|
|
61
|
+
from :current
|
|
62
|
+
select "h2_section[first:1]"
|
|
63
|
+
include "all"
|
|
64
|
+
set
|
|
65
|
+
|
|
66
|
+
from({ type: "explicit", key: "guide" })
|
|
67
|
+
select "h2_section"
|
|
68
|
+
include "all"
|
|
69
|
+
append
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
result = MarkdownComposer.compose(sources: sources, plan: plan)
|
|
73
|
+
|
|
74
|
+
puts result.markdown
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`MarkdownComposer.compose` always receives source content through the `sources:` keyword. Plan-row `inline` sources are the exception: they embed small source content directly in the plan.
|
|
78
|
+
|
|
79
|
+
## Sources
|
|
80
|
+
|
|
81
|
+
Runtime sources are plain hashes or values produced by `MarkdownComposer.source_list`.
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
sources = MarkdownComposer.source_list do
|
|
85
|
+
current current_markdown
|
|
86
|
+
explicit :guide, guide_markdown
|
|
87
|
+
explicit :faq, faq_markdown
|
|
88
|
+
inherited :site_intro, inherited_markdown
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This returns the same shape as:
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
[
|
|
96
|
+
{ "key" => "current", "type" => "current", "markdown" => current_markdown },
|
|
97
|
+
{ "key" => "guide", "type" => "explicit", "markdown" => guide_markdown },
|
|
98
|
+
{ "key" => "faq", "type" => "explicit", "markdown" => faq_markdown },
|
|
99
|
+
{ "key" => "site_intro", "type" => "inherited", "markdown" => inherited_markdown }
|
|
100
|
+
]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Supported runtime source types:
|
|
104
|
+
|
|
105
|
+
| Type | Purpose |
|
|
106
|
+
| --- | --- |
|
|
107
|
+
| `current` | Main document supplied by the caller. |
|
|
108
|
+
| `explicit` | Named source selected by key. |
|
|
109
|
+
| `inherited` | Named source resolved by the host application. |
|
|
110
|
+
|
|
111
|
+
Plan-only source references such as `previous`, `buffer`, and `inline` are not runtime source records.
|
|
112
|
+
|
|
113
|
+
## Plans
|
|
114
|
+
|
|
115
|
+
Plans can be authored with the Ruby DSL:
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
plan = MarkdownComposer.plan do
|
|
119
|
+
output :markdown
|
|
120
|
+
|
|
121
|
+
from :current
|
|
122
|
+
select 'heading_2_section where none(title:equals("Table of Contents"))'
|
|
123
|
+
include "heading_title"
|
|
124
|
+
set
|
|
125
|
+
|
|
126
|
+
from :buffer
|
|
127
|
+
select 'heading_2 where text:starts_with("1.1")'
|
|
128
|
+
remove_buffer_target
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Structured config is also supported for storage, import, and export:
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
config = {
|
|
136
|
+
"output" => "markdown",
|
|
137
|
+
"compose" => [
|
|
138
|
+
{
|
|
139
|
+
"source" => "current",
|
|
140
|
+
"select" => "h2_section[first:1]",
|
|
141
|
+
"include" => "all",
|
|
142
|
+
"action" => "set"
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
result = MarkdownComposer.compose(sources: sources, config: config)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
YAML and JSON helpers are available:
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
plan = MarkdownComposer.parse_yaml(yaml)
|
|
154
|
+
yaml = MarkdownComposer.to_yaml(plan)
|
|
155
|
+
|
|
156
|
+
plan = MarkdownComposer.parse_json(json)
|
|
157
|
+
json = MarkdownComposer.to_json(plan)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Row-style hashes are accepted for spreadsheet or GUI workflows:
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
plan = MarkdownComposer.parse_rows([
|
|
164
|
+
{ "Source" => "current", "Select" => "h2_section[first:1]", "Include" => "all", "Action" => "set" }
|
|
165
|
+
])
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Public API
|
|
169
|
+
|
|
170
|
+
Main entry points:
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
MarkdownComposer.compose(sources:, config: nil, plan: nil, options: {})
|
|
174
|
+
MarkdownComposer.validate(config:, sources: [], options: {})
|
|
175
|
+
MarkdownComposer.normalise(config_or_rows)
|
|
176
|
+
MarkdownComposer.normalize(config_or_rows)
|
|
177
|
+
MarkdownComposer.capabilities(options: {})
|
|
178
|
+
MarkdownComposer.plan(&block)
|
|
179
|
+
MarkdownComposer.source_list(&block)
|
|
180
|
+
MarkdownComposer.parse_yaml(yaml_string)
|
|
181
|
+
MarkdownComposer.parse_json(json_string)
|
|
182
|
+
MarkdownComposer.parse_rows(row_hashes)
|
|
183
|
+
MarkdownComposer.to_yaml(plan)
|
|
184
|
+
MarkdownComposer.to_json(plan)
|
|
185
|
+
MarkdownComposer.to_rows(plan)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`validate` returns `valid`, the normalized plan, diagnostics, and errors. `capabilities` returns JSON-compatible metadata for building host UI controls.
|
|
189
|
+
|
|
190
|
+
## Actions And Transforms
|
|
191
|
+
|
|
192
|
+
Compose actions place selected content into the composition buffer:
|
|
193
|
+
|
|
194
|
+
- `set`, `append`, `prepend`
|
|
195
|
+
- `insert_before`, `insert_after`, `insert_between`
|
|
196
|
+
- `replace`, `copy`, `move`
|
|
197
|
+
- `modify`
|
|
198
|
+
- `remove_buffer_target`, `transform_buffer_target`
|
|
199
|
+
|
|
200
|
+
Transforms run after composition or inside `modify`/`transform_buffer_target` rows. Supported transform families include heading numbering, heading levels, text replacement, link handling, content insertion/removal, dedupe, ordering, sanitise, and adapter-owned transforms.
|
|
201
|
+
|
|
202
|
+
Use `MarkdownComposer.capabilities` as the source of truth for enabled actions, tokens, transform modes, required options, aliases, support status, and policy gates.
|
|
203
|
+
|
|
204
|
+
## Framework Integration
|
|
205
|
+
|
|
206
|
+
Markdown Composer does not ship Rails, ViewComponent, React, Vue, or other framework-specific integrations.
|
|
207
|
+
|
|
208
|
+
Host applications are responsible for:
|
|
209
|
+
|
|
210
|
+
- resolving documents into plain Markdown/HTML source hashes;
|
|
211
|
+
- enforcing authorization, tenant, visibility, and workflow rules;
|
|
212
|
+
- preparing source Markdown when templates or variables are involved;
|
|
213
|
+
- sanitizing output and resolving assets;
|
|
214
|
+
- rendering previews and mapping diagnostics into host UI.
|
|
215
|
+
|
|
216
|
+
Rails, Hotwire, React, Vue, CLIs, and scripts should wrap the same headless API.
|
|
217
|
+
|
|
218
|
+
## Validation And Diagnostics
|
|
219
|
+
|
|
220
|
+
Validation covers portable Composer configuration: source references, selectors, include rules, targets, actions, transforms, output formats, diagnostics, and policy-gated features.
|
|
221
|
+
|
|
222
|
+
Example diagnostic:
|
|
223
|
+
|
|
224
|
+
```ruby
|
|
225
|
+
{
|
|
226
|
+
code: "transform.option_missing",
|
|
227
|
+
message: "Missing required option from",
|
|
228
|
+
path: "transform[0].options.from",
|
|
229
|
+
severity: :error,
|
|
230
|
+
details: nil
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Current diagnostic families include:
|
|
235
|
+
|
|
236
|
+
- `action.*`
|
|
237
|
+
- `json.*`
|
|
238
|
+
- `output.*`
|
|
239
|
+
- `source.*`
|
|
240
|
+
- `take.*`
|
|
241
|
+
- `target.*`
|
|
242
|
+
- `token.*`
|
|
243
|
+
- `transform.*`
|
|
244
|
+
- `where.*`
|
|
245
|
+
- `yaml.*`
|
|
246
|
+
|
|
247
|
+
## Format Support
|
|
248
|
+
|
|
249
|
+
Markdown is the source-fidelity format. HTML input is accepted through a best-effort HTML-to-Markdown path and may lose fidelity for structures that do not map cleanly to Markdown.
|
|
250
|
+
|
|
251
|
+
| From | To | Status | Notes |
|
|
252
|
+
| --- | --- | --- | --- |
|
|
253
|
+
| Markdown | Markdown | Supported | Source-fidelity selection and transforms. |
|
|
254
|
+
| Markdown | HTML | Supported | Rendered via CommonMarker when available, fallback renderer otherwise. |
|
|
255
|
+
| HTML | Markdown | Best effort | Uses Nokogiri-backed conversion. |
|
|
256
|
+
| HTML | HTML | Best effort | Converts through Markdown, then renders back to HTML. |
|
|
257
|
+
|
|
258
|
+
Host-specific sanitization, custom attributes, and asset policies should live outside the gem.
|
|
259
|
+
|
|
260
|
+
## Development
|
|
261
|
+
|
|
262
|
+
Run the test suite:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
bundle exec rake test
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Build the gem package:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
gem build markdown_composer.gemspec
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The release test suite covers public helper APIs, diagnostics, transform capability coverage, Markdown/HTML conversion fixtures, syntax checks, and coupling guards.
|
|
275
|
+
|
|
276
|
+
## License
|
|
277
|
+
|
|
278
|
+
Markdown Composer is released under the MIT License. See `LICENSE.txt`.
|
data/ROADMAP.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Roadmap
|
|
2
|
+
|
|
3
|
+
This roadmap tracks planned improvements for Markdown Composer after the `0.7.0` standalone gem release. It is intentionally focused on the gem's portable contract: syntax, normalization, validation, diagnostics, composition, and framework-independent helper APIs.
|
|
4
|
+
|
|
5
|
+
## 1. Canonical Field Syntax Printer
|
|
6
|
+
|
|
7
|
+
**Status:** Planned
|
|
8
|
+
|
|
9
|
+
Add a helper that converts normalized selector/config hashes back into canonical field syntax strings.
|
|
10
|
+
|
|
11
|
+
Example target API:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
MarkdownComposer.to_field_syntax(selector_hash)
|
|
15
|
+
# => 'heading_2_section where none(title:equals("Table of Contents"))'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This belongs in the gem because the gem owns the field syntax contract.
|
|
19
|
+
|
|
20
|
+
Expected behavior:
|
|
21
|
+
|
|
22
|
+
- Produce deterministic, valid field syntax from normalized hashes.
|
|
23
|
+
- Prefer canonical formatting over preserving original spacing or quote style.
|
|
24
|
+
- Support imports, YAML-to-field workflows, examples, migrations, and normalized presets.
|
|
25
|
+
- Include focused tests for selectors, `where` clauses, predicates, grouping, and invalid input diagnostics.
|
|
26
|
+
|
|
27
|
+
Non-goal:
|
|
28
|
+
|
|
29
|
+
- Restoring the exact user-entered text after normalization. Typos, spacing, quote choices, aliases, and partially invalid syntax cannot be reconstructed from a normalized hash.
|
|
30
|
+
|
|
31
|
+
## 2. Extension Registry API
|
|
32
|
+
|
|
33
|
+
**Status:** Planned
|
|
34
|
+
|
|
35
|
+
Add a supported extension API for adding Composer vocabulary and behavior without changing the core row syntax.
|
|
36
|
+
|
|
37
|
+
Target extension areas:
|
|
38
|
+
|
|
39
|
+
- Unit tokens, such as custom blocks, callouts, figures, or inline elements.
|
|
40
|
+
- `where` fields, predicates, and groups.
|
|
41
|
+
- Take operators, such as custom windowing or proximity rules.
|
|
42
|
+
- Sources, targets, transforms, and field interpolation providers.
|
|
43
|
+
|
|
44
|
+
Expected behavior:
|
|
45
|
+
|
|
46
|
+
- Extensions register metadata for validation, diagnostics, and GUI capability discovery.
|
|
47
|
+
- Runtime behavior is registered explicitly through handlers or callables.
|
|
48
|
+
- Unknown or disabled extension tokens fail validation instead of being accepted silently.
|
|
49
|
+
- Host-specific behavior stays in host apps or separate extension gems, not in the core gem.
|
|
50
|
+
|
|
51
|
+
## 3. Authored Field Preservation Guidance
|
|
52
|
+
|
|
53
|
+
**Status:** Planned
|
|
54
|
+
|
|
55
|
+
Document and support the expected editing workflow for host applications:
|
|
56
|
+
|
|
57
|
+
- Preserve authored field strings when exact edit-screen fidelity matters.
|
|
58
|
+
- Normalize field strings, YAML, JSON, and row input for validation and execution.
|
|
59
|
+
- Use future canonical pretty-printer helpers when generated syntax is acceptable.
|
|
60
|
+
|
|
61
|
+
For Rails and other host frameworks, this means the application should keep the user's authored field string while editing, then pass normalized data to the gem for validation and composition.
|
|
62
|
+
|
|
63
|
+
Expected outcome:
|
|
64
|
+
|
|
65
|
+
- Clear README examples showing authored string preservation alongside normalized configs.
|
|
66
|
+
- Validation guidance explaining what the gem can verify and what the host framework must verify.
|
|
67
|
+
- Tests proving canonical output is stable without implying exact round-trip text restoration.
|
|
68
|
+
|
|
69
|
+
## 4. Maintenance And Bug Fixes
|
|
70
|
+
|
|
71
|
+
**Status:** Ongoing
|
|
72
|
+
|
|
73
|
+
Maintain the gem through focused bug fixes, compatibility updates, and regression tests.
|
|
74
|
+
|
|
75
|
+
Priorities:
|
|
76
|
+
|
|
77
|
+
- Fix parser, normalization, validation, diagnostics, composition, and transform bugs.
|
|
78
|
+
- Add regression tests for confirmed bugs.
|
|
79
|
+
- Keep supported Ruby versions and runtime dependencies current.
|
|
80
|
+
- Avoid breaking stored configs or public APIs without a planned migration note.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Markdown Composer Architecture
|
|
3
|
+
type: architecture
|
|
4
|
+
status: current
|
|
5
|
+
updated: 2026-06-01
|
|
6
|
+
description: Public architecture overview for Markdown Composer internals and host boundaries.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Markdown Composer Architecture
|
|
10
|
+
|
|
11
|
+
### References
|
|
12
|
+
|
|
13
|
+
| What | Where |
|
|
14
|
+
|------|-------|
|
|
15
|
+
| Concepts | [./_md_composer_concepts.md](./_md_composer_concepts.md) |
|
|
16
|
+
| API reference | [./reference/md_composer_reference_api.md](./reference/md_composer_reference_api.md) |
|
|
17
|
+
| Registries reference | [./reference/md_composer_reference_registries.md](./reference/md_composer_reference_registries.md) |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Execution Pipeline
|
|
22
|
+
|
|
23
|
+
Markdown Composer runs in five broad stages:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
Parse config -> Validate -> Index sources -> Execute compose rows -> Run transforms
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The result contains Markdown, optional HTML, diagnostics, errors, and optional stage snapshots.
|
|
30
|
+
|
|
31
|
+
## 2. Source Indexing
|
|
32
|
+
|
|
33
|
+
Each source is converted into a document index. The index exposes nodes and sections so selectors can match headings, sections, paragraphs, lists, tables, code blocks, data blocks, and other units.
|
|
34
|
+
|
|
35
|
+
HTML input is converted to Markdown as a best-effort/lossy source format. Markdown input is the source-fidelity format.
|
|
36
|
+
|
|
37
|
+
## 3. Composition Buffer
|
|
38
|
+
|
|
39
|
+
Compose actions write to a `CompositionBuffer`. The buffer is Markdown text plus origin metadata. Target matching runs against a fresh index of the current buffer.
|
|
40
|
+
|
|
41
|
+
This detail matters: derived `data_record` and `data_value` nodes can be produced from `data_path(...)`, but ordinary target matching reparses the serialized Markdown buffer. Treat those tokens as structured-output metadata unless a host adapter materializes them as matchable buffer nodes.
|
|
42
|
+
|
|
43
|
+
## 4. Transforms
|
|
44
|
+
|
|
45
|
+
Transforms run through `TransformRunner` against the buffer. Top-level transforms come from `config["transform"]`. Row-level transforms are used by `modify` and `transform_buffer_target`.
|
|
46
|
+
|
|
47
|
+
## 5. Host Boundary
|
|
48
|
+
|
|
49
|
+
The gem owns parsing, validation, indexing, composition, transforms, and capabilities metadata. The host owns source lookup, UI, persistence, authorization, preview shell, and any policy choices for adapter/policy-gated features.
|
|
50
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Markdown Composer Cheatsheet
|
|
3
|
+
type: cheatsheet
|
|
4
|
+
status: current
|
|
5
|
+
updated: 2026-06-01
|
|
6
|
+
description: Public quick-reference for Markdown Composer syntax and docs navigation.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Markdown Composer Cheatsheet
|
|
10
|
+
|
|
11
|
+
### References
|
|
12
|
+
|
|
13
|
+
| What | Where |
|
|
14
|
+
|------|-------|
|
|
15
|
+
| Compose anatomy | [./compose/md_composer_compose_anatomy.md](./compose/md_composer_compose_anatomy.md) |
|
|
16
|
+
| Transform anatomy | [./transform/md_composer_transform_anatomy.md](./transform/md_composer_transform_anatomy.md) |
|
|
17
|
+
| Unit tokens | [./reference/md_composer_unit_tokens.md](./reference/md_composer_unit_tokens.md) |
|
|
18
|
+
| Take modifiers | [./reference/md_composer_take.md](./reference/md_composer_take.md) |
|
|
19
|
+
| Where conditions | [./reference/md_composer_where.md](./reference/md_composer_where.md) |
|
|
20
|
+
| Examples | [./examples/md_composer_examples_readme.md](./examples/md_composer_examples_readme.md) |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. Compose Row
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
Source -> Select -> Include -> Action -> Target
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
| Field | Common values |
|
|
31
|
+
|-------|---------------|
|
|
32
|
+
| Source | `current`, `explicit`, `previous`, `buffer`, `inline` |
|
|
33
|
+
| Select | `all`, `heading_2_section[first:1]`, `paragraph where text:contains("x")` |
|
|
34
|
+
| Include | `all`, `content`, `heading_title`, `paragraph[first:2]`, `data_path("title")` |
|
|
35
|
+
| Action | `set`, `append`, `prepend`, `replace`, `modify`, `remove_buffer_target`, `transform_buffer_target` |
|
|
36
|
+
| Target | blank, `whole output`, `start`, `end`, selector, `before ...`, `after ...`, `between ... and ...`, `in_place` |
|
|
37
|
+
|
|
38
|
+
## 2. Transform Row
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
Scope -> Transform -> Mode -> Options
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Field | Common values |
|
|
45
|
+
|-------|---------------|
|
|
46
|
+
| Scope | `output`, `heading_2_section`, `paragraph where text:contains("x")` |
|
|
47
|
+
| Transform | `replace_text`, `heading_numbers`, `heading_levels`, `links`, `remove_empty` |
|
|
48
|
+
| Mode | transform-specific, such as `literal`, `rebuild`, `promote`, `rewrite_url` |
|
|
49
|
+
| Options | hash or string, such as `{ "from" => "Draft", "to" => "Final" }` or `from:"Draft"; to:"Final"` |
|
|
50
|
+
|
|
51
|
+
## 3. Select Syntax
|
|
52
|
+
|
|
53
|
+
| Need | Syntax |
|
|
54
|
+
|------|--------|
|
|
55
|
+
| First H2 section | `heading_2_section[first:1]` |
|
|
56
|
+
| H2 section by title | `heading_2_section where title:equals("API")` |
|
|
57
|
+
| Paragraphs containing text | `paragraph where text:contains("warning")` |
|
|
58
|
+
| Positions 2 and 3 | `heading_2_section[position:2,3]` |
|
|
59
|
+
| Range | `heading_2_section[range:2..4]` |
|
|
60
|
+
| Nested include | `heading_2_section { heading_title; paragraph[first:1] }` |
|
|
61
|
+
|
|
62
|
+
## 4. High-Value Docs
|
|
63
|
+
|
|
64
|
+
| Need | Read |
|
|
65
|
+
|------|------|
|
|
66
|
+
| How compose rows work | [Compose anatomy](./compose/md_composer_compose_anatomy.md) |
|
|
67
|
+
| Which unit tokens exist | [Unit tokens](./reference/md_composer_unit_tokens.md) |
|
|
68
|
+
| How `[first:1]`, `[position:2]`, and ranges work | [Take modifiers](./reference/md_composer_take.md) |
|
|
69
|
+
| How `where title:contains(...)` works | [Where conditions](./reference/md_composer_where.md) |
|
|
70
|
+
| How `modify` works | [Actions](./compose/md_composer_compose_actions.md) and [modify example](./examples/md_composer_example_modify.md) |
|
|
71
|
+
| How targets work | [Targets](./compose/md_composer_compose_targets.md) |
|
|
72
|
+
| How transform options work | [Options](./transform/md_composer_transform_options.md) |
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Markdown Composer Concepts
|
|
3
|
+
type: guide
|
|
4
|
+
status: current
|
|
5
|
+
updated: 2026-06-01
|
|
6
|
+
description: Human-facing concepts for Markdown Composer plans, rows, buffers, selectors, and transforms.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Markdown Composer Concepts
|
|
10
|
+
|
|
11
|
+
### References
|
|
12
|
+
|
|
13
|
+
| What | Where |
|
|
14
|
+
|------|-------|
|
|
15
|
+
| Compose anatomy | [./compose/md_composer_compose_anatomy.md](./compose/md_composer_compose_anatomy.md) |
|
|
16
|
+
| Buffer guide | [./compose/md_composer_compose_buffer.md](./compose/md_composer_compose_buffer.md) |
|
|
17
|
+
| Transform anatomy | [./transform/md_composer_transform_anatomy.md](./transform/md_composer_transform_anatomy.md) |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Plans
|
|
22
|
+
|
|
23
|
+
A plan is the normalized instruction set Markdown Composer executes. Public configs usually use:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
{
|
|
27
|
+
"compose" => [ ... ],
|
|
28
|
+
"transform" => [ ... ],
|
|
29
|
+
"output" => "markdown"
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`compose` builds the buffer. `transform` reshapes the buffer. `output` is `markdown` or `html`.
|
|
34
|
+
|
|
35
|
+
## 2. Compose Rows
|
|
36
|
+
|
|
37
|
+
Compose rows answer: what content should be selected, what part of it should be kept, and where should it go?
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
Source -> Select -> Include -> Action -> Target
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The most common row is: select a section from the current source, include all of it, and `set` the buffer to that content.
|
|
44
|
+
|
|
45
|
+
## 3. The Buffer
|
|
46
|
+
|
|
47
|
+
The buffer is the working Markdown document being built. Early rows create it. Later rows append, prepend, replace, move, remove, or transform content already in it.
|
|
48
|
+
|
|
49
|
+
Use `source: "buffer"` when a row should select from the current buffer instead of from the original source.
|
|
50
|
+
|
|
51
|
+
## 4. Transform Rows
|
|
52
|
+
|
|
53
|
+
Transform rows answer: what part of the composed buffer should change, what transform should run, which mode should it use, and what options does it need?
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
Scope -> Transform -> Mode -> Options
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Use top-level transforms for whole-output cleanup. Use row-level transforms with `modify` or `transform_buffer_target` when the change belongs to a specific compose action.
|
|
60
|
+
|
|
61
|
+
## 5. Diagnostics
|
|
62
|
+
|
|
63
|
+
Markdown Composer prefers diagnostics over silent guessing. A config can be syntactically valid but still produce warnings, such as empty selections or empty destructive targets.
|
|
64
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Markdown Composer Dev Guide
|
|
3
|
+
type: dev_guide
|
|
4
|
+
status: current
|
|
5
|
+
updated: 2026-06-01
|
|
6
|
+
description: Public developer guide for testing, validating, and maintaining Markdown Composer docs and examples.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Markdown Composer Dev Guide
|
|
10
|
+
|
|
11
|
+
### References
|
|
12
|
+
|
|
13
|
+
| What | Where |
|
|
14
|
+
|------|-------|
|
|
15
|
+
| API reference | [./reference/md_composer_reference_api.md](./reference/md_composer_reference_api.md) |
|
|
16
|
+
| Capabilities reference | [./reference/md_composer_reference_capabilities.md](./reference/md_composer_reference_capabilities.md) |
|
|
17
|
+
| AI source map | [./ai/md_composer_ai_source_map.md](./ai/md_composer_ai_source_map.md) |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Source Of Truth
|
|
22
|
+
|
|
23
|
+
For runtime behavior, trust gem code and tests first. For prose, this public `docs/` tree is the reader-facing set. The `docs/ai/` files map back to the audited canonical source material used to create these docs.
|
|
24
|
+
|
|
25
|
+
## 2. Validate Examples
|
|
26
|
+
|
|
27
|
+
Use focused Ruby probes for every runnable snippet:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
bundle exec ruby -Ilib -rmarkdown_composer -e 'puts MarkdownComposer::VERSION'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For broad safety:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bundle exec rake test
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 3. Writing Rules
|
|
40
|
+
|
|
41
|
+
Every public guide should include:
|
|
42
|
+
|
|
43
|
+
| Requirement | Reason |
|
|
44
|
+
|-------------|--------|
|
|
45
|
+
| Syntax anatomy | Readers need to see the shape before memorizing values. |
|
|
46
|
+
| Valid forms | Avoids guesswork between strings, hashes, YAML, and Ruby. |
|
|
47
|
+
| Runnable example | Keeps docs grounded. |
|
|
48
|
+
| Expected output | Makes behavior inspectable. |
|
|
49
|
+
| Common mistakes | Turns diagnostics into learning. |
|
|
50
|
+
| Related docs | Keeps pages focused without hiding depth. |
|
|
51
|
+
|
|
52
|
+
## 4. Public Boundary
|
|
53
|
+
|
|
54
|
+
Keep core gem docs framework-independent. Hosts can provide source pickers, persistence, policy gates, and preview UI, but Markdown Composer itself remains headless.
|
|
55
|
+
|