releasehx 0.1.2 → 0.2.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 +4 -4
- data/README.adoc +363 -330
- data/build/docs/_config.yml +1 -0
- data/build/docs/_release_index.adoc +3 -2
- data/build/docs/config-reference.adoc +197 -10
- data/build/docs/config-reference.json +56 -7
- data/build/docs/index.adoc +315 -59
- data/build/docs/landing.adoc +1 -1
- data/build/docs/manpage.adoc +2 -2
- data/build/docs/release-procedure.adoc +365 -0
- data/build/docs/release-procedure.html +87 -0
- data/build/docs/releasehx.1 +17 -5
- data/build/docs/sample-config.yml +14 -7
- data/lib/releasehx/cli.rb +5 -2
- data/lib/releasehx/configuration.rb +0 -1
- data/lib/releasehx/generated.rb +1 -1
- data/lib/releasehx/mcp/assets/agent-config-guide.md +1 -1
- data/lib/releasehx/mcp/assets/config-def.yml +122 -6
- data/lib/releasehx/mcp/assets/config-reference.adoc +197 -10
- data/lib/releasehx/mcp/assets/config-reference.json +56 -7
- data/lib/releasehx/mcp/assets/sample-config.yml +14 -7
- data/lib/releasehx/mcp/server.rb +0 -1
- data/lib/releasehx/ops/enrich_ops.rb +161 -55
- data/lib/releasehx/ops/template_ops.rb +1 -1
- data/lib/releasehx/rhyml/adapter.rb +0 -3
- data/lib/releasehx/rhyml/templates/bootstrap-overrides.css +15 -0
- data/lib/releasehx/rhyml/templates/changelog.adoc.liquid +2 -0
- data/lib/releasehx/rhyml/templates/changelog.html.liquid +6 -4
- data/lib/releasehx/rhyml/templates/changelog.md.liquid +1 -0
- data/lib/releasehx/rhyml/templates/embedded.css.liquid +263 -0
- data/lib/releasehx/rhyml/templates/entry.adoc.liquid +1 -0
- data/lib/releasehx/rhyml/templates/entry.html.liquid +21 -20
- data/lib/releasehx/rhyml/templates/entry.md.liquid +15 -21
- data/lib/releasehx/rhyml/templates/head-parser.liquid +6 -2
- data/lib/releasehx/rhyml/templates/header.liquid +13 -4
- data/lib/releasehx/rhyml/templates/history.html.liquid +152 -33
- data/lib/releasehx/rhyml/templates/metadata-entry.adoc.liquid +83 -38
- data/lib/releasehx/rhyml/templates/metadata-entry.html.liquid +60 -1
- data/lib/releasehx/rhyml/templates/metadata-entry.md.liquid +65 -113
- data/lib/releasehx/rhyml/templates/metadata-note.adoc.liquid +83 -38
- data/lib/releasehx/rhyml/templates/metadata-note.html.liquid +59 -22
- data/lib/releasehx/rhyml/templates/metadata-note.md.liquid +68 -23
- data/lib/releasehx/rhyml/templates/note.html.liquid +25 -19
- data/lib/releasehx/rhyml/templates/note.md.liquid +44 -26
- data/lib/releasehx/rhyml/templates/release-notes.adoc.liquid +2 -0
- data/lib/releasehx/rhyml/templates/release-notes.html.liquid +6 -4
- data/lib/releasehx/rhyml/templates/release-notes.md.liquid +1 -0
- data/lib/releasehx/rhyml/templates/release.adoc.liquid +2 -0
- data/lib/releasehx/rhyml/templates/release.md.liquid +8 -7
- data/lib/releasehx/rhyml/templates/rhyml-change.yaml.liquid +36 -36
- data/lib/releasehx/rhyml/templates/wrapper.html.liquid +103 -0
- data/lib/releasehx/sgyml/helpers.rb +0 -2
- data/lib/releasehx/transforms/adf_to_markdown.rb +1 -1
- data/lib/releasehx/version.rb +0 -2
- data/lib/releasehx.rb +2 -2
- data/specs/data/config-def.yml +122 -6
- metadata +48 -25
- data/build/docs/schemagraphy_readme.html +0 -0
- data/build/docs/sourcerer_readme.html +0 -46
- data/lib/schemagraphy/attribute_resolver.rb +0 -48
- data/lib/schemagraphy/cfgyml/definition.rb +0 -90
- data/lib/schemagraphy/cfgyml/doc_builder.rb +0 -52
- data/lib/schemagraphy/cfgyml/path_reference.rb +0 -24
- data/lib/schemagraphy/data_query/json_pointer.rb +0 -42
- data/lib/schemagraphy/loader.rb +0 -59
- data/lib/schemagraphy/regexp_utils.rb +0 -235
- data/lib/schemagraphy/safe_expression.rb +0 -189
- data/lib/schemagraphy/schema_utils.rb +0 -124
- data/lib/schemagraphy/tag_utils.rb +0 -32
- data/lib/schemagraphy/templating.rb +0 -104
- data/lib/schemagraphy.rb +0 -17
- data/lib/sourcerer/builder.rb +0 -120
- data/lib/sourcerer/jekyll/bootstrapper.rb +0 -78
- data/lib/sourcerer/jekyll/liquid/file_system.rb +0 -74
- data/lib/sourcerer/jekyll/liquid/filters.rb +0 -215
- data/lib/sourcerer/jekyll/liquid/tags.rb +0 -44
- data/lib/sourcerer/jekyll/monkeypatches.rb +0 -73
- data/lib/sourcerer/jekyll.rb +0 -26
- data/lib/sourcerer/plaintext_converter.rb +0 -75
- data/lib/sourcerer/templating.rb +0 -190
- data/lib/sourcerer.rb +0 -322
|
@@ -34,7 +34,16 @@ module ReleaseHx
|
|
|
34
34
|
when :html
|
|
35
35
|
# Direct Liquid template rendering to HTML
|
|
36
36
|
html_content = DraftOps.process_template_content(release: release, config: config, format: :html)
|
|
37
|
-
|
|
37
|
+
|
|
38
|
+
# Wrap HTML with Bootstrap styling if enabled
|
|
39
|
+
enriched = if config && config.dig('modes', 'html_wrap') != false
|
|
40
|
+
ReleaseHx.logger.debug('Applying HTML wrapper')
|
|
41
|
+
wrap_html(html_content, config)
|
|
42
|
+
else
|
|
43
|
+
html_content
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
WriteOps.safe_write(outpath, enriched)
|
|
38
47
|
when :pdf
|
|
39
48
|
# Two-stage process: RHYML → AsciiDoc → PDF
|
|
40
49
|
asciidoc_content = DraftOps.process_template_content(release: release, config: config, format: :adoc)
|
|
@@ -49,7 +58,7 @@ module ReleaseHx
|
|
|
49
58
|
# Generates rich-text output from source draft files of various formats.
|
|
50
59
|
#
|
|
51
60
|
# Automatically detects the input file type and applies the appropriate conversion
|
|
52
|
-
# strategy, supporting YAML (via RHYML)
|
|
61
|
+
# strategy, supporting YAML (via RHYML) and AsciiDoc source formats.
|
|
53
62
|
#
|
|
54
63
|
# @param file_path [String] The path to the source draft file.
|
|
55
64
|
# @param format [Symbol] The target output format (:html or :pdf).
|
|
@@ -75,9 +84,6 @@ module ReleaseHx
|
|
|
75
84
|
# RHYML/YAML files: load as Release object and use Liquid templates
|
|
76
85
|
release = load_rhyml_from_yaml(file_path, config: config)
|
|
77
86
|
enrich_from_rhyml(release: release, config: config, format: format, outpath: outpath, force: true)
|
|
78
|
-
when '.md'
|
|
79
|
-
# Markdown files: use native converter
|
|
80
|
-
convert_markdown(file_path, format: format, config: config, outpath: outpath, force: true)
|
|
81
87
|
when '.adoc'
|
|
82
88
|
# AsciiDoc files: use native converter
|
|
83
89
|
convert_asciidoc(file_path, format: format, config: config, outpath: outpath, force: true)
|
|
@@ -86,70 +92,41 @@ module ReleaseHx
|
|
|
86
92
|
end
|
|
87
93
|
end
|
|
88
94
|
|
|
89
|
-
# Converts
|
|
95
|
+
# Converts AsciiDoc content or files to rich-text formats using configured engines.
|
|
90
96
|
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
# @param format [Symbol] The target output format (:html or :pdf).
|
|
96
|
-
# @param config [ReleaseHx::Configuration] Reserved for future use.
|
|
97
|
-
# @param outpath [String] The target output file path.
|
|
98
|
-
# @param force [Boolean] Reserved for future use.
|
|
99
|
-
# @return [String] The path to the generated output file.
|
|
100
|
-
# rubocop:disable Lint/UnusedMethodArgument
|
|
101
|
-
def self.convert_markdown file_path, format:, config:, outpath:, force:
|
|
102
|
-
# NOTE: config and force parameters are currently unused but kept for API consistency
|
|
103
|
-
content = File.read(file_path)
|
|
104
|
-
|
|
105
|
-
case format.to_sym
|
|
106
|
-
when :html
|
|
107
|
-
require 'kramdown'
|
|
108
|
-
enriched = Kramdown::Document.new(content).to_html
|
|
109
|
-
WriteOps.safe_write(outpath, enriched)
|
|
110
|
-
when :pdf
|
|
111
|
-
# Use Tilt with Pandoc for direct Markdown to PDF conversion
|
|
112
|
-
require 'tilt'
|
|
113
|
-
require 'tilt/pandoc'
|
|
114
|
-
|
|
115
|
-
template = Tilt::PandocTemplate.new(file_path)
|
|
116
|
-
enriched = template.render(nil, to: 'pdf')
|
|
117
|
-
WriteOps.safe_write(outpath, enriched)
|
|
118
|
-
ReleaseHx.logger.info("PDF generated via Tilt/Pandoc: #{outpath}")
|
|
119
|
-
else
|
|
120
|
-
raise "Unsupported format for Markdown: #{format}"
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
outpath
|
|
124
|
-
end
|
|
125
|
-
# rubocop:enable Lint/UnusedMethodArgument
|
|
126
|
-
|
|
127
|
-
# Converts AsciiDoc content or files to rich-text formats using Asciidoctor.
|
|
97
|
+
# Accepts either file paths or raw content strings as input. Selects appropriate
|
|
98
|
+
# converter engine based on config.conversions.engines.html/pdf settings with intelligent defaults:
|
|
99
|
+
# - AsciiDoc → HTML: asciidoctor-html5s (default)
|
|
100
|
+
# - AsciiDoc → PDF: asciidoctor-pdf (default)
|
|
128
101
|
#
|
|
129
|
-
#
|
|
130
|
-
# for HTML generation and Asciidoctor-PDF for direct PDF output.
|
|
102
|
+
# HTML output can be wrapped with Bootstrap CSS when config.modes.html_wrap is enabled.
|
|
131
103
|
#
|
|
132
104
|
# @param file_path_or_content [String] File path or raw AsciiDoc content.
|
|
133
105
|
# @param format [Symbol] The target output format (:html or :pdf).
|
|
134
106
|
# @param outpath [String] The target output file path.
|
|
135
|
-
# @param config [ReleaseHx::Configuration]
|
|
136
|
-
# @param force [Boolean]
|
|
107
|
+
# @param config [ReleaseHx::Configuration] Configuration for engines and wrapping options.
|
|
108
|
+
# @param force [Boolean] Whether to overwrite existing files.
|
|
137
109
|
# @return [String] The path to the generated output file.
|
|
138
|
-
# rubocop:disable Lint/UnusedMethodArgument
|
|
139
110
|
def self.convert_asciidoc file_path_or_content, format:, outpath:, config: nil, force: nil
|
|
140
|
-
# NOTE: config and force parameters are currently unused but kept for API consistency
|
|
141
111
|
# Determine if input is file path or raw content
|
|
142
112
|
is_file = file_path_or_content.is_a?(String) && File.exist?(file_path_or_content)
|
|
143
113
|
content = is_file ? File.read(file_path_or_content) : file_path_or_content
|
|
144
114
|
|
|
145
115
|
case format.to_sym
|
|
146
116
|
when :html
|
|
147
|
-
|
|
148
|
-
|
|
117
|
+
engine = resolve_engine(format: :html, source_format: :asciidoc, config: config)
|
|
118
|
+
html_fragment = convert_with_engine(content, engine: engine, format: :html)
|
|
119
|
+
|
|
120
|
+
# Wrap HTML with Bootstrap styling if enabled
|
|
121
|
+
enriched = if config && config.dig('modes', 'html_wrap') != false
|
|
122
|
+
ReleaseHx.logger.debug('Applying wrapper to AsciiDoc-derived HTML')
|
|
123
|
+
wrap_html(html_fragment, config)
|
|
124
|
+
else
|
|
125
|
+
html_fragment
|
|
126
|
+
end
|
|
149
127
|
when :pdf
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
Asciidoctor.convert(content, to_file: outpath, safe: :safe, backend: 'pdf')
|
|
128
|
+
engine = resolve_engine(format: :pdf, source_format: :asciidoc, config: config)
|
|
129
|
+
convert_with_engine(content, engine: engine, format: :pdf, outpath: outpath)
|
|
153
130
|
return outpath
|
|
154
131
|
else
|
|
155
132
|
raise "Unsupported format for AsciiDoc: #{format}"
|
|
@@ -163,7 +140,6 @@ module ReleaseHx
|
|
|
163
140
|
WriteOps.safe_write(outpath, enriched)
|
|
164
141
|
outpath
|
|
165
142
|
end
|
|
166
|
-
# rubocop:enable Lint/UnusedMethodArgument
|
|
167
143
|
|
|
168
144
|
# Loads RHYML data from a YAML file and creates a Release object.
|
|
169
145
|
#
|
|
@@ -217,5 +193,135 @@ module ReleaseHx
|
|
|
217
193
|
filename = SchemaGraphy::Templating.render_field_if_template(filename_template, context)
|
|
218
194
|
File.join(output_dir, enrich_dir, filename.strip)
|
|
219
195
|
end
|
|
196
|
+
|
|
197
|
+
# Wraps an HTML fragment with Bootstrap CSS and semantic HTML structure.
|
|
198
|
+
#
|
|
199
|
+
# Takes a raw HTML fragment and wraps it with proper DOCTYPE, head section with Bootstrap CDN link, and body tag.
|
|
200
|
+
# Uses the wrapper.html.liquid template for markup generation.
|
|
201
|
+
#
|
|
202
|
+
# @param html_fragment [String] The raw HTML content to wrap.
|
|
203
|
+
# @param config [ReleaseHx::Configuration] Configuration object for title and settings.
|
|
204
|
+
# @return [String] Complete HTML document with Bootstrap styling.
|
|
205
|
+
def self.wrap_html html_fragment, config
|
|
206
|
+
# Extract and parse framework setting
|
|
207
|
+
framework_spec = config.dig('history', 'html_framework') || 'bare'
|
|
208
|
+
framework_name, framework_version = parse_framework_spec(framework_spec)
|
|
209
|
+
|
|
210
|
+
template_path = WriteOps.resolve_template_path('wrapper.html.liquid', config)
|
|
211
|
+
|
|
212
|
+
template_vars = {
|
|
213
|
+
'config' => config,
|
|
214
|
+
'content' => html_fragment,
|
|
215
|
+
'framework' => framework_name,
|
|
216
|
+
'framework_version' => framework_version
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
WriteOps.process_template(template_path, template_vars, config)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Parses framework specification string into name and version.
|
|
223
|
+
#
|
|
224
|
+
# @param spec [String] Framework specification like 'bootstrap5' or 'bootstrap:5.3.0'
|
|
225
|
+
# @return [Array<String, String>] Tuple of [framework_name, framework_version]
|
|
226
|
+
def self.parse_framework_spec spec
|
|
227
|
+
case spec.to_s
|
|
228
|
+
when /^(.+):(.+)$/
|
|
229
|
+
# Format: "bootstrap:5.3.0"
|
|
230
|
+
[::Regexp.last_match(1), ::Regexp.last_match(2)]
|
|
231
|
+
when 'bootstrap5'
|
|
232
|
+
['bootstrap', '5.3.0']
|
|
233
|
+
when 'bootstrap4'
|
|
234
|
+
['bootstrap', '4.6.2']
|
|
235
|
+
else
|
|
236
|
+
['bare', nil]
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Resolves the appropriate conversion engine based on format and source.
|
|
241
|
+
#
|
|
242
|
+
# Applies intelligent defaults based on source format:
|
|
243
|
+
# - AsciiDoc → HTML: asciidoctor-html5s
|
|
244
|
+
# - AsciiDoc → PDF: asciidoctor-pdf
|
|
245
|
+
# - Markdown → HTML: kramdown
|
|
246
|
+
# - Markdown → PDF: pandoc
|
|
247
|
+
#
|
|
248
|
+
# @param format [Symbol] Output format (:html or :pdf)
|
|
249
|
+
# @param source_format [Symbol] Source format (:asciidoc or :markdown)
|
|
250
|
+
# @param config [Hash] Configuration hash
|
|
251
|
+
# @return [String] Engine name
|
|
252
|
+
def self.resolve_engine format:, source_format:, config: nil
|
|
253
|
+
# Check for explicit engine configuration
|
|
254
|
+
explicit_engine = config&.dig('conversions', 'engines', format.to_s)
|
|
255
|
+
return explicit_engine if explicit_engine
|
|
256
|
+
|
|
257
|
+
# Apply intelligent defaults based on source format
|
|
258
|
+
case [source_format, format]
|
|
259
|
+
when %i[asciidoc html]
|
|
260
|
+
'asciidoctor-html5s'
|
|
261
|
+
when %i[asciidoc pdf]
|
|
262
|
+
'asciidoctor-pdf'
|
|
263
|
+
when %i[markdown html]
|
|
264
|
+
'kramdown'
|
|
265
|
+
when %i[markdown pdf]
|
|
266
|
+
'pandoc'
|
|
267
|
+
else
|
|
268
|
+
raise "Unsupported conversion: #{source_format} → #{format}"
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Converts content using the specified engine.
|
|
273
|
+
#
|
|
274
|
+
# @param content [String] Source content to convert
|
|
275
|
+
# @param engine [String] Engine name (e.g., 'asciidoctor-html5s', 'asciidoctor-pdf')
|
|
276
|
+
# @param format [Symbol] Output format (:html or :pdf)
|
|
277
|
+
# @param outpath [String, nil] Output file path (required for PDF)
|
|
278
|
+
# @return [String] Converted content (for HTML) or output path (for PDF)
|
|
279
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
|
280
|
+
def self.convert_with_engine content, engine:, format:, outpath: nil
|
|
281
|
+
case engine
|
|
282
|
+
when 'asciidoctor-html5', 'asciidoctor-html5s'
|
|
283
|
+
require 'asciidoctor'
|
|
284
|
+
backend = engine.split('-').last # 'html5' or 'html5s'
|
|
285
|
+
|
|
286
|
+
if backend == 'html5s'
|
|
287
|
+
begin
|
|
288
|
+
require 'asciidoctor-html5s'
|
|
289
|
+
ReleaseHx.logger.debug('Using asciidoctor-html5s backend for semantic HTML5')
|
|
290
|
+
rescue LoadError
|
|
291
|
+
ReleaseHx.logger.warn('asciidoctor-html5s not available, falling back to html5')
|
|
292
|
+
backend = 'html5'
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
Asciidoctor.convert(content, safe: :safe, backend: backend)
|
|
297
|
+
|
|
298
|
+
when 'asciidoctor-pdf'
|
|
299
|
+
require 'asciidoctor'
|
|
300
|
+
require 'asciidoctor-pdf'
|
|
301
|
+
ReleaseHx.logger.debug('Using asciidoctor-pdf for PDF generation')
|
|
302
|
+
Asciidoctor.convert(content, to_file: outpath, safe: :safe, backend: 'pdf')
|
|
303
|
+
outpath
|
|
304
|
+
|
|
305
|
+
when 'asciidoctor-web-pdf'
|
|
306
|
+
require 'asciidoctor'
|
|
307
|
+
require 'asciidoctor-web-pdf'
|
|
308
|
+
ReleaseHx.logger.debug('Using asciidoctor-web-pdf for PDF generation')
|
|
309
|
+
Asciidoctor.convert(content, to_file: outpath, safe: :safe, backend: 'web-pdf')
|
|
310
|
+
outpath
|
|
311
|
+
|
|
312
|
+
when 'kramdown'
|
|
313
|
+
require 'kramdown'
|
|
314
|
+
ReleaseHx.logger.debug('Using Kramdown for HTML conversion')
|
|
315
|
+
Kramdown::Document.new(content).to_html
|
|
316
|
+
|
|
317
|
+
when 'pandoc'
|
|
318
|
+
# Future implementation - would shell out to pandoc
|
|
319
|
+
raise 'Pandoc engine not yet implemented'
|
|
320
|
+
|
|
321
|
+
else
|
|
322
|
+
raise "Unsupported engine: #{engine}"
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
|
220
326
|
end
|
|
221
327
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* Bootstrap Overrides for ReleaseHx */
|
|
2
|
+
/* Users can override this file by placing bootstrap-overrides.css in their _templates directory */
|
|
3
|
+
|
|
4
|
+
/* Dark Mode Fixes */
|
|
5
|
+
@media (prefers-color-scheme: dark) {
|
|
6
|
+
/* Fix .bg-light in card headers - use semantic card background instead */
|
|
7
|
+
.card-header.bg-light {
|
|
8
|
+
background-color: var(--bs-card-cap-bg) !important;
|
|
9
|
+
border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.container-lg {
|
|
14
|
+
max-width: 900px;
|
|
15
|
+
}
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
{%- assign dflt_next_header_level = level | plus: 1 -%}
|
|
5
5
|
{%- assign item_count = 0 %}
|
|
6
6
|
|
|
7
|
-
<
|
|
8
|
-
|
|
7
|
+
<section class="changelog-section py-4 px-3 mb-4 rounded">
|
|
8
|
+
{%- include header.liquid format=format level=level text=content_config.head %}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
{%- embed section-text.liquid %}
|
|
11
11
|
|
|
12
|
+
<div class="changelog-content mt-4">
|
|
12
13
|
{%- embed groupings.liquid %}
|
|
13
|
-
</div>
|
|
14
|
+
</div>
|
|
15
|
+
</section>
|
|
14
16
|
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/* ReleaseHx Embedded CSS - {{ theme | capitalize }} Theme */
|
|
2
|
+
|
|
3
|
+
/* CSS Custom Properties - Theme: {{ theme }} */
|
|
4
|
+
:root {
|
|
5
|
+
{%- if theme == 'compact' %}
|
|
6
|
+
--release-spacing: 1.5rem;
|
|
7
|
+
--section-spacing: 1rem;
|
|
8
|
+
--item-spacing: 0.5rem;
|
|
9
|
+
--container-padding: 1rem;
|
|
10
|
+
--heading-margin: 0.75rem;
|
|
11
|
+
{%- elsif theme == 'detailed' %}
|
|
12
|
+
--release-spacing: 4rem;
|
|
13
|
+
--section-spacing: 3rem;
|
|
14
|
+
--item-spacing: 1.5rem;
|
|
15
|
+
--container-padding: 3rem;
|
|
16
|
+
--heading-margin: 2rem;
|
|
17
|
+
{%- else %}
|
|
18
|
+
--release-spacing: 3rem;
|
|
19
|
+
--section-spacing: 2.5rem;
|
|
20
|
+
--item-spacing: 1rem;
|
|
21
|
+
--container-padding: 2rem;
|
|
22
|
+
--heading-margin: 1.5rem;
|
|
23
|
+
{%- endif %}
|
|
24
|
+
|
|
25
|
+
/* Light Theme Colors */
|
|
26
|
+
--primary-color: #0d6efd;
|
|
27
|
+
--success-color: #198754;
|
|
28
|
+
--info-color: #0dcaf0;
|
|
29
|
+
--warning-color: #ffc107;
|
|
30
|
+
--danger-color: #dc3545;
|
|
31
|
+
--light-bg: #f8f9fa;
|
|
32
|
+
--white-bg: #ffffff;
|
|
33
|
+
--text-color: #212529;
|
|
34
|
+
--text-muted: #6c757d;
|
|
35
|
+
--border-color: #dee2e6;
|
|
36
|
+
--border-radius: 0.375rem;
|
|
37
|
+
--shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
38
|
+
--shadow-hover: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Dark Theme Colors */
|
|
42
|
+
@media (prefers-color-scheme: dark) {
|
|
43
|
+
:root {
|
|
44
|
+
--primary-color: #6ea8fe;
|
|
45
|
+
--success-color: #75b798;
|
|
46
|
+
--info-color: #6edff6;
|
|
47
|
+
--warning-color: #ffda6a;
|
|
48
|
+
--danger-color: #ea868f;
|
|
49
|
+
--light-bg: #212529;
|
|
50
|
+
--white-bg: #2b3035;
|
|
51
|
+
--text-color: #ffffff;
|
|
52
|
+
--text-muted: #adb5bd;
|
|
53
|
+
--border-color: #495057;
|
|
54
|
+
--shadow: 0 1px 3px rgba(255, 255, 255, 0.1);
|
|
55
|
+
--shadow-hover: 0 2px 8px rgba(255, 255, 255, 0.15);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Manual Dark Theme Override */
|
|
60
|
+
.dark-theme {
|
|
61
|
+
--primary-color: #6ea8fe;
|
|
62
|
+
--success-color: #75b798;
|
|
63
|
+
--info-color: #6edff6;
|
|
64
|
+
--warning-color: #ffda6a;
|
|
65
|
+
--danger-color: #ea868f;
|
|
66
|
+
--light-bg: #212529;
|
|
67
|
+
--white-bg: #2b3035;
|
|
68
|
+
--text-color: #ffffff;
|
|
69
|
+
--text-muted: #adb5bd;
|
|
70
|
+
--border-color: #495057;
|
|
71
|
+
--shadow: 0 1px 3px rgba(255, 255, 255, 0.1);
|
|
72
|
+
--shadow-hover: 0 2px 8px rgba(255, 255, 255, 0.15);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Base Styles */
|
|
76
|
+
body {
|
|
77
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
78
|
+
line-height: 1.6;
|
|
79
|
+
color: var(--text-color);
|
|
80
|
+
background-color: var(--light-bg);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Container and Layout */
|
|
84
|
+
.release-history {
|
|
85
|
+
max-width: 1200px;
|
|
86
|
+
margin: 0 auto;
|
|
87
|
+
padding: var(--container-padding);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.release-section {
|
|
91
|
+
margin-bottom: var(--release-spacing);
|
|
92
|
+
padding: var(--container-padding);
|
|
93
|
+
background-color: var(--white-bg);
|
|
94
|
+
border-radius: var(--border-radius);
|
|
95
|
+
box-shadow: var(--shadow);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Section Styling */
|
|
99
|
+
.changelog-section, .notes-section {
|
|
100
|
+
margin-bottom: var(--section-spacing);
|
|
101
|
+
padding: var(--container-padding);
|
|
102
|
+
border-radius: var(--border-radius);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.changelog-section {
|
|
106
|
+
border-left: 4px solid var(--success-color);
|
|
107
|
+
background-color: rgba(25, 135, 84, 0.02);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.notes-section {
|
|
111
|
+
border-left: 4px solid var(--info-color);
|
|
112
|
+
background-color: rgba(13, 202, 240, 0.02);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Release Notes */
|
|
116
|
+
.release-note {
|
|
117
|
+
margin-bottom: var(--item-spacing);
|
|
118
|
+
transition: box-shadow 0.2s ease;
|
|
119
|
+
border: 1px solid var(--border-color);
|
|
120
|
+
border-radius: var(--border-radius);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.release-note:hover {
|
|
124
|
+
box-shadow: var(--shadow-hover);
|
|
125
|
+
border-color: var(--primary-color);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.card-header {
|
|
129
|
+
background-color: var(--light-bg);
|
|
130
|
+
border-bottom: 1px solid #dee2e6;
|
|
131
|
+
padding: 1rem 1.25rem;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.card-body {
|
|
135
|
+
padding: 1.25rem;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.card-footer {
|
|
139
|
+
background-color: var(--light-bg);
|
|
140
|
+
border-top: 1px solid #dee2e6;
|
|
141
|
+
padding: 0.75rem 1.25rem;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* Changelog Entries */
|
|
145
|
+
.change-entry {
|
|
146
|
+
margin-bottom: var(--item-spacing);
|
|
147
|
+
padding: 0.75rem 1rem;
|
|
148
|
+
border: 1px solid var(--border-color);
|
|
149
|
+
border-radius: var(--border-radius);
|
|
150
|
+
transition: all 0.15s ease;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.change-entry:hover {
|
|
154
|
+
background-color: var(--light-bg);
|
|
155
|
+
border-color: var(--primary-color);
|
|
156
|
+
transform: translateX(4px);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Typography */
|
|
160
|
+
h1, h2, h3, h4, h5, h6 {
|
|
161
|
+
margin-bottom: var(--heading-margin);
|
|
162
|
+
line-height: 1.2;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
{%- if theme == 'compact' %}
|
|
166
|
+
h1 { font-size: 1.75rem; }
|
|
167
|
+
h2 { font-size: 1.5rem; }
|
|
168
|
+
h3 { font-size: 1.25rem; }
|
|
169
|
+
{%- elsif theme == 'detailed' %}
|
|
170
|
+
h1 { font-size: 3rem; }
|
|
171
|
+
h2 { font-size: 2.5rem; }
|
|
172
|
+
h3 { font-size: 2rem; }
|
|
173
|
+
{%- endif %}
|
|
174
|
+
|
|
175
|
+
/* Badges and Labels */
|
|
176
|
+
.badge {
|
|
177
|
+
font-size: 0.75em;
|
|
178
|
+
padding: 0.35em 0.65em;
|
|
179
|
+
border-radius: 0.375rem;
|
|
180
|
+
font-weight: 600;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.badge-primary { background-color: var(--primary-color); color: white; }
|
|
184
|
+
.badge-success { background-color: var(--success-color); color: white; }
|
|
185
|
+
.badge-info { background-color: var(--info-color); color: white; }
|
|
186
|
+
.badge-warning { background-color: var(--warning-color); color: black; }
|
|
187
|
+
.badge-danger { background-color: var(--danger-color); color: white; }
|
|
188
|
+
|
|
189
|
+
/* Metadata Display */
|
|
190
|
+
.change-metadata {
|
|
191
|
+
display: flex;
|
|
192
|
+
flex-wrap: wrap;
|
|
193
|
+
gap: 0.5rem;
|
|
194
|
+
align-items: center;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.metadata-item {
|
|
198
|
+
display: inline-flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
gap: 0.25rem;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* Links */
|
|
204
|
+
a {
|
|
205
|
+
color: var(--primary-color);
|
|
206
|
+
text-decoration: none;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
a:hover {
|
|
210
|
+
text-decoration: underline;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Responsive Design */
|
|
214
|
+
@media (max-width: 768px) {
|
|
215
|
+
.release-history {
|
|
216
|
+
padding: 1rem;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.release-section {
|
|
220
|
+
padding: 1.5rem;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.change-entry {
|
|
224
|
+
padding: 0.5rem 0.75rem;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
{%- if theme == 'detailed' %}
|
|
228
|
+
h1 { font-size: 2rem; }
|
|
229
|
+
h2 { font-size: 1.75rem; }
|
|
230
|
+
h3 { font-size: 1.5rem; }
|
|
231
|
+
{%- endif %}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* Print Styles */
|
|
235
|
+
@media print {
|
|
236
|
+
body {
|
|
237
|
+
background: white;
|
|
238
|
+
color: black;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.release-section {
|
|
242
|
+
box-shadow: none;
|
|
243
|
+
border: 1px solid var(--border-color);
|
|
244
|
+
break-inside: avoid;
|
|
245
|
+
page-break-inside: avoid;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.release-note {
|
|
249
|
+
break-inside: avoid;
|
|
250
|
+
page-break-inside: avoid;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
a {
|
|
254
|
+
color: black;
|
|
255
|
+
text-decoration: none;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
a:after {
|
|
259
|
+
content: " (" attr(href) ")";
|
|
260
|
+
font-size: 0.8em;
|
|
261
|
+
color: #666;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
{%- embed metadata-entry.html.liquid -%}
|
|
2
1
|
{%- assign frame = frame %}
|
|
3
2
|
{%- case frame %}
|
|
4
3
|
{%- when "unordered" %}
|
|
@@ -10,29 +9,31 @@
|
|
|
10
9
|
{%- assign item_class = "list-group-item border-0 px-0" %}
|
|
11
10
|
{%- assign bullet = "" %}
|
|
12
11
|
{%- else %}
|
|
13
|
-
{%- assign list_class = "list-
|
|
14
|
-
{%- assign item_class = "
|
|
12
|
+
{%- assign list_class = "list-group" %}
|
|
13
|
+
{%- assign item_class = "list-group-item px-3 py-2" %}
|
|
15
14
|
{%- assign bullet = "" %}
|
|
16
15
|
{%- endcase %}
|
|
17
16
|
|
|
18
17
|
<div class="change-entry {{ item_class }}" id="entry-{{ change.chid }}">
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
</
|
|
35
|
-
|
|
18
|
+
<div class="d-flex align-items-start justify-content-between">
|
|
19
|
+
<div class="flex-grow-1">
|
|
20
|
+
<p class="change-summary mb-2">
|
|
21
|
+
<span class="text-body">{{ bullet }}{{ change.summ | trim | pasterize }}</span>
|
|
22
|
+
</p>
|
|
23
|
+
{%- if change_metadata != "" -%}
|
|
24
|
+
<div class="change-metadata">
|
|
25
|
+
{{ change_metadata | trim }}
|
|
26
|
+
</div>
|
|
27
|
+
{%- endif %}
|
|
28
|
+
</div>
|
|
29
|
+
{%- if change.note %}
|
|
30
|
+
<div class="note-link ms-3 ps-2 text-nowrap">
|
|
31
|
+
<a href="#note-{{ change.chid }}" class="badge bg-warning text-decoration-none" title="Has a release note">
|
|
32
|
+
<i class="fa fa-sticky-note-o"></i>
|
|
33
|
+
<small>NOTE</small>
|
|
34
|
+
</a>
|
|
36
35
|
</div>
|
|
36
|
+
{%- endif %}
|
|
37
|
+
</div>
|
|
37
38
|
</div>
|
|
38
39
|
|