charming 0.1.2 → 0.1.3
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/lib/charming/application.rb +3 -3
- data/lib/charming/controller/class_methods.rb +2 -2
- data/lib/charming/controller/command_palette.rb +2 -2
- data/lib/charming/controller/rendering.rb +2 -2
- data/lib/charming/controller/session_state.rb +1 -1
- data/lib/charming/generators/component_generator.rb +1 -1
- data/lib/charming/generators/templates/app/application.template +1 -1
- data/lib/charming/generators/templates/app/layout.template +3 -6
- data/lib/charming/generators/templates/app/view.template +1 -1
- data/lib/charming/generators/templates/component/component.rb.template +1 -1
- data/lib/charming/generators/templates/screen/view.rb.template +1 -1
- data/lib/charming/generators/templates/view/view.rb.template +1 -1
- data/lib/charming/internal/renderer/differential.rb +13 -5
- data/lib/charming/internal/terminal/tty_backend.rb +22 -2
- data/lib/charming/presentation/component.rb +3 -5
- data/lib/charming/presentation/components/activity_indicator.rb +173 -134
- data/lib/charming/presentation/components/command_palette.rb +94 -96
- data/lib/charming/presentation/components/command_palette_modal.rb +33 -0
- data/lib/charming/presentation/components/empty_state.rb +47 -49
- data/lib/charming/presentation/components/form/builder.rb +52 -54
- data/lib/charming/presentation/components/form/confirm.rb +49 -51
- data/lib/charming/presentation/components/form/field.rb +94 -96
- data/lib/charming/presentation/components/form/input.rb +53 -55
- data/lib/charming/presentation/components/form/note.rb +27 -29
- data/lib/charming/presentation/components/form/select.rb +84 -86
- data/lib/charming/presentation/components/form/textarea.rb +67 -69
- data/lib/charming/presentation/components/form.rb +120 -122
- data/lib/charming/presentation/components/keyboard_handler.rb +41 -43
- data/lib/charming/presentation/components/list.rb +123 -125
- data/lib/charming/presentation/components/markdown.rb +21 -23
- data/lib/charming/presentation/components/modal.rb +46 -48
- data/lib/charming/presentation/components/progressbar.rb +51 -53
- data/lib/charming/presentation/components/spinner.rb +40 -42
- data/lib/charming/presentation/components/table.rb +109 -111
- data/lib/charming/presentation/components/text_area.rb +219 -221
- data/lib/charming/presentation/components/text_input.rb +120 -122
- data/lib/charming/presentation/components/viewport.rb +218 -220
- data/lib/charming/presentation/layout/builder.rb +64 -66
- data/lib/charming/presentation/layout/overlay.rb +48 -50
- data/lib/charming/presentation/layout/pane.rb +122 -118
- data/lib/charming/presentation/layout/rect.rb +14 -16
- data/lib/charming/presentation/layout/screen_layout.rb +40 -42
- data/lib/charming/presentation/layout/split.rb +101 -103
- data/lib/charming/presentation/layout.rb +28 -30
- data/lib/charming/presentation/markdown/block_renderers.rb +94 -96
- data/lib/charming/presentation/markdown/inline_renderers.rb +52 -54
- data/lib/charming/presentation/markdown/render_context.rb +12 -14
- data/lib/charming/presentation/markdown/renderer.rb +84 -86
- data/lib/charming/presentation/markdown/syntax_highlighter.rb +57 -59
- data/lib/charming/presentation/markdown.rb +4 -6
- data/lib/charming/presentation/template_view.rb +22 -24
- data/lib/charming/presentation/templates/erb_handler.rb +4 -6
- data/lib/charming/presentation/templates.rb +47 -49
- data/lib/charming/presentation/ui/ansi_codes.rb +66 -68
- data/lib/charming/presentation/ui/ansi_slicer.rb +67 -69
- data/lib/charming/presentation/ui/border.rb +24 -26
- data/lib/charming/presentation/ui/border_painter.rb +37 -39
- data/lib/charming/presentation/ui/canvas.rb +59 -61
- data/lib/charming/presentation/ui/style.rb +173 -175
- data/lib/charming/presentation/ui/theme.rb +133 -135
- data/lib/charming/presentation/ui/width.rb +12 -14
- data/lib/charming/presentation/ui.rb +69 -71
- data/lib/charming/presentation/view.rb +103 -105
- data/lib/charming/runtime.rb +23 -10
- data/lib/charming/version.rb +1 -1
- data/lib/charming.rb +3 -2
- metadata +2 -1
|
@@ -1,67 +1,65 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Charming
|
|
4
|
-
module
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
end
|
|
4
|
+
module Markdown
|
|
5
|
+
# InlineRenderer dispatches Kramdown inline-level elements (text, strong, em,
|
|
6
|
+
# codespan, link, line break, HTML entity) to their individual rendering handlers.
|
|
7
|
+
# Handlers are built once at construction as a frozen hash of element-type symbols
|
|
8
|
+
# to callables.
|
|
9
|
+
class InlineRenderer
|
|
10
|
+
# *renderer* is the parent Renderer (used to render nested inlines and look up styles).
|
|
11
|
+
def initialize(renderer:)
|
|
12
|
+
@renderer = renderer
|
|
13
|
+
build_handlers
|
|
14
|
+
end
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
# Renders *element* using the handler registered for `element.type`. Unknown types
|
|
17
|
+
# fall through to `render_unknown`.
|
|
18
|
+
def render(element, context:)
|
|
19
|
+
handler = @handlers[element.type] || method(:render_unknown)
|
|
20
|
+
handler.call(element, context)
|
|
21
|
+
end
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
private
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
# The frozen hash of element-type → handler mapping.
|
|
26
|
+
attr_reader :handlers
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
# Builds the handler hash for text, strong, em, codespan, link, br, and entity.
|
|
29
|
+
def build_handlers
|
|
30
|
+
r = @renderer
|
|
31
|
+
@handlers = {
|
|
32
|
+
text: ->(element, _context) { element.value.to_s },
|
|
33
|
+
strong: ->(element, context) { render_styled(element, context, :markdown_strong) { |s| s.bold } },
|
|
34
|
+
em: ->(element, context) { render_styled(element, context, :markdown_emphasis) { |s| s.italic } },
|
|
35
|
+
codespan: ->(element, _context) { r.style_for(:markdown_inline_code, fallback: r.theme_style(:warn)).render(element.value.to_s) },
|
|
36
|
+
a: ->(element, context) { send(:render_link, element, context) },
|
|
37
|
+
br: ->(_element, _context) { "\n" },
|
|
38
|
+
entity: ->(element, _context) { element.value.respond_to?(:char) ? element.value.char : element.value.to_s }
|
|
39
|
+
}.freeze
|
|
40
|
+
end
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
# Renders a styled inline (strong/em) by first rendering children, then applying
|
|
43
|
+
# the theme style and the block-form (e.g., `bold`/`italic`) decoration.
|
|
44
|
+
def render_styled(element, context, style_name)
|
|
45
|
+
rendered = @renderer.render_inlines(element.children, width: context.width)
|
|
46
|
+
style = @renderer.style_for(style_name, fallback: yield(@renderer.theme_style(:text)))
|
|
47
|
+
style.render(rendered)
|
|
48
|
+
end
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
# Renders a Markdown link as "label <href>" (URL omitted when empty), styled with
|
|
51
|
+
# the markdown_link theme token or the info+underline fallback.
|
|
52
|
+
def render_link(element, context)
|
|
53
|
+
label = @renderer.render_inlines(element.children, width: context.width)
|
|
54
|
+
href = element.attr["href"].to_s
|
|
55
|
+
rendered = href.empty? ? label : "#{label} <#{href}>"
|
|
56
|
+
@renderer.style_for(:markdown_link, fallback: @renderer.theme_style(:info).underline).render(rendered)
|
|
57
|
+
end
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
end
|
|
59
|
+
# Fallback for unknown inline types: returns the value when there are no children,
|
|
60
|
+
# otherwise recurses into the children.
|
|
61
|
+
def render_unknown(element, context)
|
|
62
|
+
element.children.empty? ? element.value.to_s : @renderer.render_inlines(element.children, width: context.width)
|
|
65
63
|
end
|
|
66
64
|
end
|
|
67
65
|
end
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Charming
|
|
4
|
-
module
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
RenderContext
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
end
|
|
4
|
+
module Markdown
|
|
5
|
+
# RenderContext carries the state needed to render nested Markdown blocks: the current
|
|
6
|
+
# list nesting depth (used for indentation) and the wrap width.
|
|
7
|
+
RenderContext = Data.define(:list_depth, :width) do
|
|
8
|
+
# Builds a new RenderContext with the given *width* and optional starting *list_depth*.
|
|
9
|
+
def self.from(width:, list_depth: 0)
|
|
10
|
+
new(list_depth: list_depth, width: width)
|
|
11
|
+
end
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
end
|
|
13
|
+
# Returns a derived context with the list depth incremented by *depth_increment*
|
|
14
|
+
# and the wrap width overridden to *width* (defaults to the current width).
|
|
15
|
+
def nested(depth_increment: 0, width: self.width)
|
|
16
|
+
self.class.new(list_depth: list_depth + depth_increment, width: width)
|
|
19
17
|
end
|
|
20
18
|
end
|
|
21
19
|
end
|
|
@@ -3,110 +3,108 @@
|
|
|
3
3
|
require "kramdown"
|
|
4
4
|
|
|
5
5
|
module Charming
|
|
6
|
-
module
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
end
|
|
6
|
+
module Markdown
|
|
7
|
+
# Renderer is the top-level Markdown-to-ANSI renderer. Parses the *content* with
|
|
8
|
+
# Kramdown, then walks the document's block and inline trees to produce styled
|
|
9
|
+
# terminal output. Code blocks are highlighted via Rouge when `syntax_highlighting`
|
|
10
|
+
# is enabled.
|
|
11
|
+
class Renderer
|
|
12
|
+
# Wrap width used by `render_rule` when no width is otherwise specified.
|
|
13
|
+
DEFAULT_RULE_WIDTH = 40
|
|
14
|
+
|
|
15
|
+
# The Markdown source, configured wrap width, theme, and syntax-highlighting flag.
|
|
16
|
+
attr_reader :content, :width, :theme, :syntax_highlighting
|
|
17
|
+
|
|
18
|
+
# *content* is the Markdown source string. *width* optionally wraps paragraphs to that
|
|
19
|
+
# many display columns. *theme* is the Charming theme used to style blocks/inlines.
|
|
20
|
+
# *syntax_highlighting* enables Rouge-backed code block highlighting (default true).
|
|
21
|
+
def initialize(content:, width: nil, theme: UI::Theme.default, syntax_highlighting: true)
|
|
22
|
+
@content = content
|
|
23
|
+
@width = width
|
|
24
|
+
@theme = theme || UI::Theme.default
|
|
25
|
+
@syntax_highlighting = syntax_highlighting
|
|
26
|
+
end
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
# Parses the content and returns the fully-rendered Markdown as a single string.
|
|
29
|
+
def render
|
|
30
|
+
document = Kramdown::Document.new(content.to_s)
|
|
31
|
+
render_blocks(document.root.children)
|
|
32
|
+
end
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
# Renders a list of Kramdown block *elements* into a string, joined by blank lines.
|
|
35
|
+
# *list_depth* is forwarded to the render context for list indentation. *width*
|
|
36
|
+
# defaults to the renderer's configured width.
|
|
37
|
+
def render_blocks(elements, list_depth: 0, width: @width)
|
|
38
|
+
context = RenderContext.from(width: width, list_depth: list_depth)
|
|
39
|
+
elements.filter_map do |element|
|
|
40
|
+
rendered = block_renderer.render(element, context: context)
|
|
41
|
+
rendered unless rendered.to_s.empty?
|
|
42
|
+
end.join("\n\n")
|
|
43
|
+
end
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
# Renders a list of Kramdown inline *elements* into a single concatenated string.
|
|
46
|
+
# *width* defaults to the renderer's configured width.
|
|
47
|
+
def render_inlines(elements, width: @width)
|
|
48
|
+
context = RenderContext.from(width: width)
|
|
49
|
+
elements.map { |element| inline_renderer.render(element, context: context) }.join
|
|
50
|
+
end
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
# Word-wraps *value* to *width* display columns (when *width* is given), preserving
|
|
53
|
+
# any ANSI styling on each line. Returns *value* unchanged when *width* is nil.
|
|
54
|
+
def wrap(value, width:)
|
|
55
|
+
return value unless width
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
value.to_s.lines(chomp: true).map { |line| wrap_line(line, width) }.join("\n")
|
|
58
|
+
end
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
# Returns the theme's style for *name* if the theme defines it, otherwise returns
|
|
61
|
+
# *fallback*. Lets views override markdown-specific theme tokens.
|
|
62
|
+
def style_for(name, fallback:)
|
|
63
|
+
return theme.public_send(name) if theme.respond_to?(name)
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
fallback
|
|
66
|
+
end
|
|
68
67
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
# Returns the theme's style for *name*, building a one-token default theme when
|
|
69
|
+
# the active theme doesn't define it. Used as a final fallback for markdown styling.
|
|
70
|
+
def theme_style(name)
|
|
71
|
+
return theme.public_send(name) if theme.respond_to?(name)
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
UI::Theme::DEFAULT_TOKENS.fetch(name).then { |token| UI::Theme.new(name => token).public_send(name) }
|
|
74
|
+
end
|
|
76
75
|
|
|
77
|
-
|
|
76
|
+
private
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
# The BlockRenderer instance, lazily built.
|
|
79
|
+
def block_renderer
|
|
80
|
+
@block_renderer ||= BlockRenderer.new(renderer: self)
|
|
81
|
+
end
|
|
83
82
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
# The InlineRenderer instance, lazily built.
|
|
84
|
+
def inline_renderer
|
|
85
|
+
@inline_renderer ||= InlineRenderer.new(renderer: self)
|
|
86
|
+
end
|
|
88
87
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
# Word-wraps a single *line* to *width* display columns using greedy space-splitting.
|
|
89
|
+
def wrap_line(line, width)
|
|
90
|
+
return line if UI::Width.measure(line) <= width
|
|
92
91
|
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
lines = []
|
|
93
|
+
current = +""
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
line.split(/\s+/).each do |word|
|
|
96
|
+
candidate = current.empty? ? word : "#{current} #{word}"
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
end
|
|
98
|
+
if !current.empty? && UI::Width.measure(candidate) > width
|
|
99
|
+
lines << current.rstrip
|
|
100
|
+
current = word
|
|
101
|
+
else
|
|
102
|
+
current = candidate
|
|
105
103
|
end
|
|
106
|
-
|
|
107
|
-
lines << current.rstrip unless current.empty?
|
|
108
|
-
lines.join("\n")
|
|
109
104
|
end
|
|
105
|
+
|
|
106
|
+
lines << current.rstrip unless current.empty?
|
|
107
|
+
lines.join("\n")
|
|
110
108
|
end
|
|
111
109
|
end
|
|
112
110
|
end
|
|
@@ -3,76 +3,74 @@
|
|
|
3
3
|
require "rouge"
|
|
4
4
|
|
|
5
5
|
module Charming
|
|
6
|
-
module
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
end
|
|
6
|
+
module Markdown
|
|
7
|
+
# SyntaxHighlighter turns a code block string into ANSI-styled terminal text using
|
|
8
|
+
# Rouge lexers. The theme provides markdown_code_* tokens for per-token styling;
|
|
9
|
+
# when a token is undefined in the theme, the highlighter falls back to a sensible
|
|
10
|
+
# base style (muted italic for comments, title for keywords, etc.).
|
|
11
|
+
class SyntaxHighlighter
|
|
12
|
+
# *theme* is the active Charming theme. Defaults to UI::Theme.default.
|
|
13
|
+
def initialize(theme: UI::Theme.default)
|
|
14
|
+
@theme = theme || UI::Theme.default
|
|
15
|
+
end
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
# Highlights *code* (using Rouge) for the given *language* (auto-detected when nil)
|
|
18
|
+
# and returns a styled multi-line string. Each Rouge token is rendered with the
|
|
19
|
+
# theme style matching its token type.
|
|
20
|
+
def render(code, language: nil)
|
|
21
|
+
lexer = lexer_for(language, code)
|
|
22
|
+
lexer.lex(code.to_s).map do |token, value|
|
|
23
|
+
style_for(token).render(value)
|
|
24
|
+
end.join
|
|
25
|
+
end
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
private
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
# The Charming theme used for token styling.
|
|
30
|
+
attr_reader :theme
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
# Picks a Rouge lexer for *language* and *code*, falling back to plain text.
|
|
33
|
+
def lexer_for(language, code)
|
|
34
|
+
Rouge::Lexer.find_fancy(language, code) || Rouge::Lexers::PlainText
|
|
35
|
+
end
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
# Returns the Charming style for a given Rouge *token*, mapping token qualifiers
|
|
38
|
+
# to theme tokens and falling back to a sensible base style per category.
|
|
39
|
+
def style_for(token)
|
|
40
|
+
name = token_name(token)
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
end
|
|
42
|
+
case name
|
|
43
|
+
when /Comment/
|
|
44
|
+
theme_style(:markdown_code_comment, fallback: theme_style(:muted).italic)
|
|
45
|
+
when /Keyword/
|
|
46
|
+
theme_style(:markdown_code_keyword, fallback: theme_style(:title))
|
|
47
|
+
when /String/
|
|
48
|
+
theme_style(:markdown_code_string, fallback: theme_style(:warn))
|
|
49
|
+
when /Number|Literal/
|
|
50
|
+
theme_style(:markdown_code_literal, fallback: theme_style(:info))
|
|
51
|
+
when /Name\.(Class|Constant|Function|Namespace)/
|
|
52
|
+
theme_style(:markdown_code_constant, fallback: theme_style(:info))
|
|
53
|
+
when /Error/
|
|
54
|
+
theme_style(:markdown_code_error, fallback: theme_style(:warn).bold)
|
|
55
|
+
else
|
|
56
|
+
theme_style(:markdown_code, fallback: theme_style(:text))
|
|
59
57
|
end
|
|
58
|
+
end
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
# Returns the qualified token name when the token object supports it, otherwise
|
|
61
|
+
# the token's default `to_s`.
|
|
62
|
+
def token_name(token)
|
|
63
|
+
return token.qualname if token.respond_to?(:qualname)
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
token.to_s
|
|
66
|
+
end
|
|
68
67
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
# Returns the theme's style for *name*, falling back to *fallback* (or a default
|
|
69
|
+
# empty style) when the theme doesn't define it.
|
|
70
|
+
def theme_style(name, fallback: nil)
|
|
71
|
+
return theme.public_send(name) if theme.respond_to?(name)
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
end
|
|
73
|
+
fallback || UI.style
|
|
76
74
|
end
|
|
77
75
|
end
|
|
78
76
|
end
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Charming
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
module Markdown
|
|
9
|
-
end
|
|
4
|
+
# Markdown is the namespace for the Markdown rendering pipeline. Parsing is delegated to
|
|
5
|
+
# Kramdown; per-block and per-inline element rendering is handled by `BlockRenderer`
|
|
6
|
+
# and `InlineRenderer`; code blocks are highlighted by `SyntaxHighlighter` (Rouge-backed).
|
|
7
|
+
module Markdown
|
|
10
8
|
end
|
|
11
9
|
end
|
|
@@ -1,34 +1,32 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Charming
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
end
|
|
4
|
+
# TemplateView wraps a resolved ERB template and exposes it as a renderable View. The
|
|
5
|
+
# template is rendered with the view's helpers (`text`, `box`, `row`, `column`, `style`,
|
|
6
|
+
# `theme`, etc.) and the view's assigns available as reader methods inside the template.
|
|
7
|
+
class TemplateView < View
|
|
8
|
+
def initialize(template:, namespace: nil, **assigns)
|
|
9
|
+
super(**assigns)
|
|
10
|
+
@template = template
|
|
11
|
+
@namespace = namespace
|
|
12
|
+
end
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
# Renders the wrapped template to a string, evaluated in the view's binding context.
|
|
15
|
+
def render
|
|
16
|
+
template.render(self).to_s
|
|
17
|
+
end
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
# Returns the binding used by ERB handlers to evaluate the template body. When *namespace*
|
|
20
|
+
# is set, the binding is created by a proc generated in the namespace's context so the
|
|
21
|
+
# template can resolve constants relative to the application.
|
|
22
|
+
def template_binding
|
|
23
|
+
return binding unless namespace
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
namespace.module_eval("->(view) { view.instance_eval { binding } }", __FILE__, __LINE__).call(self)
|
|
26
|
+
end
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
private
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
end
|
|
30
|
+
attr_reader :template, :namespace
|
|
33
31
|
end
|
|
34
32
|
end
|
|
@@ -3,12 +3,10 @@
|
|
|
3
3
|
require "erb"
|
|
4
4
|
|
|
5
5
|
module Charming
|
|
6
|
-
module
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
ERB.new(File.read(path), trim_mode: "-").result(view.template_binding)
|
|
11
|
-
end
|
|
6
|
+
module Templates
|
|
7
|
+
class ErbHandler
|
|
8
|
+
def self.render(path, view)
|
|
9
|
+
ERB.new(File.read(path), trim_mode: "-").result(view.template_binding)
|
|
12
10
|
end
|
|
13
11
|
end
|
|
14
12
|
end
|