diff --git a/lib/slideck/converter.rb b/lib/slideck/converter.rb index 218a3fd..c46a16b 100644 --- a/lib/slideck/converter.rb +++ b/lib/slideck/converter.rb @@ -30,15 +30,17 @@ def initialize(markdown_parser, color: nil) # the content to convert # @param [Hash, String, Symbol] symbols # the converted content symbols + # @param [Hash{Symbol => Array, String, Symbol}] theme + # the converted content theme # @param [Integer] width # the slide width # # @return [String] # # @api public - def convert(content, symbols: nil, width: nil) + def convert(content, symbols: nil, theme: nil, width: nil) @markdown_parser.parse( - content, color: @color, symbols: symbols, width: width) + content, color: @color, symbols: symbols, theme: theme, width: width) end end # Converter end # Slideck diff --git a/lib/slideck/metadata.rb b/lib/slideck/metadata.rb index 0b2743c..37c6577 100644 --- a/lib/slideck/metadata.rb +++ b/lib/slideck/metadata.rb @@ -82,6 +82,11 @@ def self.define_meta(key) # @return [Hash, String, Symbol] define_meta :symbols + # The theme configuration + # + # @return [Hash{Symbol => Array, String, Symbol}] + define_meta :theme + # Create a Metadata instance # # @param [Hash{Symbol => Object}] metadata diff --git a/lib/slideck/metadata_defaults.rb b/lib/slideck/metadata_defaults.rb index ac7a07f..2ddb3c0 100644 --- a/lib/slideck/metadata_defaults.rb +++ b/lib/slideck/metadata_defaults.rb @@ -52,7 +52,8 @@ def create_defaults footer: default_footer, margin: @margin[0, 0, 0, 0], pager: default_pager, - symbols: :unicode + symbols: :unicode, + theme: {} }.freeze end diff --git a/lib/slideck/renderer.rb b/lib/slideck/renderer.rb index 08c681f..3002414 100644 --- a/lib/slideck/renderer.rb +++ b/lib/slideck/renderer.rb @@ -87,9 +87,9 @@ def clear # # @api private def render_content(slide) - alignment, margin, symbols = - *select_metadata(slide[:metadata], :align, :margin, :symbols) - converted = convert_markdown(slide[:content], margin, symbols) + alignment, margin, symbols, theme = + *select_metadata(slide[:metadata], :align, :margin, :symbols, :theme) + converted = convert_markdown(slide[:content], margin, symbols, theme) render_section(converted.lines, alignment, margin) end @@ -107,8 +107,9 @@ def render_footer(slide_metadata) return if (text = footer_metadata[:text]).empty? alignment = footer_metadata[:align] || @metadata.footer[:align] - margin, symbols = *select_metadata(slide_metadata, :margin, :symbols) - converted = convert_markdown(text, margin, symbols).chomp + margin, symbols, theme = + *select_metadata(slide_metadata, :margin, :symbols, :theme) + converted = convert_markdown(text, margin, symbols, theme).chomp render_section(converted.lines, alignment, margin) end @@ -130,9 +131,10 @@ def render_pager(slide_metadata, current_num, num_of_slides) return if (text = pager_metadata[:text]).empty? alignment = pager_metadata[:align] || @metadata.pager[:align] - margin, symbols = *select_metadata(slide_metadata, :margin, :symbols) + margin, symbols, theme = + *select_metadata(slide_metadata, :margin, :symbols, :theme) formatted_text = format(text, page: current_num, total: num_of_slides) - converted = convert_markdown(formatted_text, margin, symbols).chomp + converted = convert_markdown(formatted_text, margin, symbols, theme).chomp render_section(converted.lines, alignment, margin) end @@ -244,12 +246,15 @@ def find_top_row(alignment, margin, num_of_lines) # the slide margin # @param [Hash, String, Symbol] symbols # the converted content symbols + # @param [Hash{Symbol => Array, String, Symbol}] theme + # the converted content theme # # @return [String] # # @api private - def convert_markdown(content, margin, symbols) - @converter.convert(content, symbols: symbols, width: slide_width(margin)) + def convert_markdown(content, margin, symbols, theme) + @converter.convert( + content, symbols: symbols, theme: theme, width: slide_width(margin)) end # Find maximum line length diff --git a/spec/fixtures/slides.md b/spec/fixtures/slides.md index 167f67f..3ad650a 100644 --- a/spec/fixtures/slides.md +++ b/spec/fixtures/slides.md @@ -4,6 +4,13 @@ footer: "**footer content**" margin: 1 2 pager: "page %d of %d" symbols: ascii +theme: + header: + - magenta + - underline + link: cyan + list: green + strong: blue --- symbols: {base: ascii, override: {arrow: ">>"}} # [Title](url) diff --git a/spec/unit/converter_spec.rb b/spec/unit/converter_spec.rb index 8ff93aa..992abf4 100644 --- a/spec/unit/converter_spec.rb +++ b/spec/unit/converter_spec.rb @@ -6,7 +6,8 @@ it "converts markdown title to terminal output" do converter = described_class.new(markdown_parser, color: true) - converted = converter.convert("# Title", symbols: :unicode, width: 20) + converted = converter.convert("# Title", symbols: :unicode, theme: {}, + width: 20) expect(converted).to eq("\e[36;1;4mTitle\e[0m\n") end @@ -14,7 +15,8 @@ it "converts markdown title to terminal output without color" do converter = described_class.new(markdown_parser, color: false) - converted = converter.convert("# Title", symbols: :unicode, width: 20) + converted = converter.convert("# Title", symbols: :unicode, theme: {}, + width: 20) expect(converted).to eq("Title\n") end @@ -23,7 +25,7 @@ converter = described_class.new(markdown_parser, color: true) converted = converter.convert("# Very Long Title", symbols: :unicode, - width: 10) + theme: {}, width: 10) expect(converted).to eq([ "\e[36;1;4mVery Long \e[0m\n", @@ -35,7 +37,7 @@ converter = described_class.new(markdown_parser, color: false) converted = converter.convert("# Very Long Title", symbols: :unicode, - width: 10) + theme: {}, width: 10) expect(converted).to eq([ "Very Long \n", @@ -46,8 +48,19 @@ it "converts a markdown list to terminal output with ascii symbols" do converter = described_class.new(markdown_parser, color: true) - converted = converter.convert("- list item", symbols: :ascii, width: 20) + converted = converter.convert("- list item", symbols: :ascii, theme: {}, + width: 20) expect(converted).to eq("\e[33m*\e[0m list item\n") end + + it "converts a markdown list to terminal output with a custom theme" do + converter = described_class.new(markdown_parser, color: true) + + converted = converter.convert("- list item", symbols: :unicode, + theme: {list: :magenta}, + width: 20) + + expect(converted).to eq("\e[35m●\e[0m list item\n") + end end diff --git a/spec/unit/metadata_converter_spec.rb b/spec/unit/metadata_converter_spec.rb index 100ba4e..5bf0a08 100644 --- a/spec/unit/metadata_converter_spec.rb +++ b/spec/unit/metadata_converter_spec.rb @@ -102,4 +102,16 @@ symbols: {base: :ascii, override: {bullet: "x"}} }) end + + it "doesn't convert :theme value" do + converter = described_class.new(alignment, margin) + + converted = converter.convert({ + theme: {list: :magenta, strong: %i[magenta bold]} + }) + + expect(converted).to eq({ + theme: {list: :magenta, strong: %i[magenta bold]} + }) + end end diff --git a/spec/unit/metadata_defaults_spec.rb b/spec/unit/metadata_defaults_spec.rb index acd8a6e..df0cc99 100644 --- a/spec/unit/metadata_defaults_spec.rb +++ b/spec/unit/metadata_defaults_spec.rb @@ -20,7 +20,8 @@ align: alignment["right", "bottom"], text: "%d / %d" }, - symbols: :unicode + symbols: :unicode, + theme: {} }) end @@ -35,7 +36,8 @@ pager: { text: "%d of %d" }, - symbols: :ascii + symbols: :ascii, + theme: {link: :magenta} }) expect(merged).to eq({ @@ -49,7 +51,8 @@ align: alignment["right", "bottom"], text: "%d of %d" }, - symbols: :ascii + symbols: :ascii, + theme: {link: :magenta} }) end @@ -65,7 +68,8 @@ pager: { align: alignment["right", "top"] }, - symbols: :ascii + symbols: :ascii, + theme: {link: :magenta} }) expect(merged).to eq({ @@ -79,7 +83,8 @@ align: alignment["right", "top"], text: "%d / %d" }, - symbols: :ascii + symbols: :ascii, + theme: {link: :magenta} }) end end diff --git a/spec/unit/metadata_spec.rb b/spec/unit/metadata_spec.rb index 36be3b9..877dffa 100644 --- a/spec/unit/metadata_spec.rb +++ b/spec/unit/metadata_spec.rb @@ -244,6 +244,20 @@ expect(metadata.symbols).to eq(:ascii) end + it "defaults :theme to an empty hash" do + metadata = described_class.from(converter, {}, defaults) + + expect(metadata.theme).to eq({}) + end + + it "creates metadata from :theme with a custom :link value" do + config = {theme: {link: :magenta}} + + metadata = described_class.from(converter, config, defaults) + + expect(metadata.theme).to eq({link: :magenta}) + end + it "raises when invalid metadata key" do config = {invalid: ""} @@ -252,6 +266,6 @@ }.to raise_error(Slideck::InvalidMetadataKeyError, "unknown 'invalid' configuration key\n" \ "Available keys are: :align, :footer, :margin, " \ - ":pager, :symbols") + ":pager, :symbols, :theme") end end diff --git a/spec/unit/parser_spec.rb b/spec/unit/parser_spec.rb index 46e5568..b6596da 100644 --- a/spec/unit/parser_spec.rb +++ b/spec/unit/parser_spec.rb @@ -2,8 +2,8 @@ RSpec.describe Slideck::Parser, "#parse" do let(:metadata_parser) { - Slideck::MetadataParser.new(::YAML, permitted_classes: [Symbol], - symbolize_names: true) + Slideck::MetadataParser.new(YAML, permitted_classes: [Symbol], + symbolize_names: true) } it "parses empty content" do @@ -80,6 +80,8 @@ margin: 2 pager: "page %d of %d" symbols: ascii + theme: + list: green EOS parser = described_class.new(StringScanner, metadata_parser) @@ -91,7 +93,8 @@ footer: "footer content", margin: 2, pager: "page %d of %d", - symbols: "ascii" + symbols: "ascii", + theme: {list: "green"} }, slides: [] }) @@ -104,6 +107,8 @@ :margin: 2 :pager: "page %d of %d" :symbols: ascii + :theme: + list: green EOS parser = described_class.new(StringScanner, metadata_parser) @@ -115,7 +120,8 @@ footer: "footer content", margin: 2, pager: "page %d of %d", - symbols: "ascii" + symbols: "ascii", + theme: {list: "green"} }, slides: [] }) @@ -128,6 +134,8 @@ margin: [1, 2] pager: "page %d of %d" symbols: ascii + theme: + list: green --- # Slide 1 --- @@ -145,7 +153,8 @@ footer: "footer content", margin: [1, 2], pager: "page %d of %d", - symbols: "ascii" + symbols: "ascii", + theme: {list: "green"} }, slides: [ {content: "# Slide 1", metadata: {}}, @@ -163,6 +172,8 @@ margin: {top: 1, left: 2} pager: "page %d of %d" symbols: ascii + theme: + list: green --- # Slide 1 --- @@ -181,7 +192,8 @@ footer: "footer content", margin: {top: 1, left: 2}, pager: "page %d of %d", - symbols: "ascii" + symbols: "ascii", + theme: {list: "green"} }, slides: [ {content: "# Slide 1", metadata: {}}, @@ -209,7 +221,7 @@ Slide 4 - ---{symbols: {override: {bullet: "x"}}} + ---{symbols: {override: {bullet: "x"}}, theme: {list: green}} Slide 5 EOS @@ -229,7 +241,8 @@ {content: "\nSlide 4\n", metadata: {pager: {text: "page %d of %d"}}}, {content: "\nSlide 5", - metadata: {symbols: {override: {bullet: "x"}}}} + metadata: {symbols: {override: {bullet: "x"}}, + theme: {list: "green"}}} ] }) end diff --git a/spec/unit/renderer_spec.rb b/spec/unit/renderer_spec.rb index 3e00b1d..292f60a 100644 --- a/spec/unit/renderer_spec.rb +++ b/spec/unit/renderer_spec.rb @@ -242,6 +242,33 @@ def build_slide_metadata(custom_metadata) ].join.inspect) end + it "renders a markdown list with a custom theme" do + metadata = build_metadata({ + theme: { + header: %i[magenta underline], + list: :green + } + }) + renderer = described_class.new(converter, ansi, cursor, metadata, + width: 20, height: 8) + + content = unindent(<<-EOS) + # List + - a + - b + - c + EOS + slide = {content: content, metadata: build_slide_metadata({})} + + expect(renderer.render(slide, 1, 4).inspect).to eq([ + "\e[1;1H\e[35;4mList\e[0m\n", + "\e[2;1H\e[32m●\e[0m a\n", + "\e[3;1H\e[32m●\e[0m b\n", + "\e[4;1H\e[32m●\e[0m c\n", + "\e[8;16H1 / 4" + ].join.inspect) + end + it "renders content with footer and page number" do metadata = build_metadata({footer: "footer"}) renderer = described_class.new(converter, ansi, cursor, metadata, @@ -283,6 +310,26 @@ def build_slide_metadata(custom_metadata) ].join.inspect) end + it "renders the footer and pager content with a custom theme" do + metadata = build_metadata({ + footer: "**footer**", + pager: "*%d / %d*", + theme: { + em: :cyan, + strong: %i[magenta underline] + } + }) + renderer = described_class.new(converter, ansi, cursor, metadata, + width: 20, height: 8) + slide = {content: "content", metadata: build_slide_metadata({})} + + expect(renderer.render(slide, 1, 4).inspect).to eq([ + "\e[1;1Hcontent\n", + "\e[8;1H\e[35;4mfooter\e[0m", + "\e[8;16H\e[36m1 / 4\e[0m" + ].join.inspect) + end + it "renders content and page number with markdown" do metadata = build_metadata({pager: "**%s of %s**"}) renderer = described_class.new(converter, ansi, cursor, metadata, @@ -480,6 +527,29 @@ def build_slide_metadata(custom_metadata) "\e[8;13H\e[33mx\e[0m 1 of 4" ].join.inspect) end + + it "renders content with global metadata theme overridden in a slide" do + metadata = build_metadata({ + footer: "**footer**", + pager: "*%d of %d*" + }) + slide_metadata = build_slide_metadata({ + theme: { + header: %w[magenta underline], + em: "cyan", + strong: "green" + } + }) + slide = {content: "# content", metadata: slide_metadata} + renderer = described_class.new(converter, ansi, cursor, metadata, + width: 20, height: 8) + + expect(renderer.render(slide, 1, 4).inspect).to eq([ + "\e[1;1H\e[35;4mcontent\e[0m\n", + "\e[8;1H\e[32mfooter\e[0m", + "\e[8;15H\e[36m1 of 4\e[0m" + ].join.inspect) + end end describe "#clear" do diff --git a/spec/unit/runner_spec.rb b/spec/unit/runner_spec.rb index 95a7dba..ef8b487 100644 --- a/spec/unit/runner_spec.rb +++ b/spec/unit/runner_spec.rb @@ -40,12 +40,12 @@ expect(output.string.inspect).to eq([ "\e[?25l\e[2J\e[1;1H", "\e[3;14H\n", - "\e[4;14H\e[36;1;4mTitle >> \e[33;4murl\e[0m\e[0m\n", + "\e[4;14H\e[35;4mTitle >> \e[36murl\e[0m\e[0m\n", "\e[5;14H\n", - "\e[6;14H\e[33m*\e[0m Item 1\n", - "\e[7;14H\e[33m*\e[0m Item 2\n", - "\e[8;14H\e[33m*\e[0m Item 3\n", - "\e[9;3H\e[33;1mfooter content\e[0m", + "\e[6;14H\e[32m*\e[0m Item 1\n", + "\e[7;14H\e[32m*\e[0m Item 2\n", + "\e[8;14H\e[32m*\e[0m Item 3\n", + "\e[9;3H\e[34mfooter content\e[0m", "\e[9;28Hpage 1 of 5", "\e[2J\e[1;1H\e[?25h" ].join.inspect)