Class: Rubino::UI::MarkdownRenderer
- Inherits:
-
Object
- Object
- Rubino::UI::MarkdownRenderer
- Defined in:
- lib/rubino/ui/markdown_renderer.rb
Overview
Renders a markdown string into a list of styled token-lines.
Output shape:
render(text) -> [LineTokens, LineTokens, ...]
LineTokens = [[String, StyleHash], ...]
StyleHash = { fg:, bg:, modifiers: [...] } (any subset; nil ≈ default)
The caller turns these into ANSI-colored strings via Pastel. Keeping the output as plain Ruby data lets the renderer be tested without a real terminal.
Coverage: headings 1-3, paragraphs, bold, italic, ‘inline code`, “`fenced“` code blocks, ordered/unordered lists (one level), block quotes, [links](url), horizontal rules. Anything unrecognized falls back to its raw text content, never blowing up.
Constant Summary collapse
- MIN_TABLE_WIDTH =
Smallest width we’ll ask TTY::Table to fit into. Below this, resize tends to raise (a column needs at least ~2 cols + borders); we clamp up to keep the headless/extreme-narrow paths from blowing up.
20- DEFAULT_WIDTH =
80
Instance Method Summary collapse
-
#initialize(width: nil) ⇒ MarkdownRenderer
constructor
A new instance of MarkdownRenderer.
- #render(text) ⇒ Object
-
#render_partial_table(lines, max_rows: nil) ⇒ Object
Render a GROWING (partial) GFM table for the streaming live region — the header/separator plus the already-completed data rows (the in-flight last row is dropped upstream by StreamingMarkdown#table_rows_so_far).
Constructor Details
#initialize(width: nil) ⇒ MarkdownRenderer
Returns a new instance of MarkdownRenderer.
46 47 48 |
# File 'lib/rubino/ui/markdown_renderer.rb', line 46 def initialize(width: nil) @width = width || detect_width end |
Instance Method Details
#render(text) ⇒ Object
50 51 52 53 54 55 56 57 58 59 |
# File 'lib/rubino/ui/markdown_renderer.rb', line 50 def render(text) return [] if text.nil? || text.to_s.strip.empty? src = unwrap_markdown_wrapper(text.to_s) doc = Kramdown::Document.new(normalize(src), input: "GFM", auto_ids: false, hard_wrap: false) block_lines(doc.root).reject { |line| line == :drop } rescue StandardError # Parser failure -> degrade to plain text rather than break the UI. text.to_s.split("\n", -1).map { |l| [[l, nil]] } end |
#render_partial_table(lines, max_rows: nil) ⇒ Object
Render a GROWING (partial) GFM table for the streaming live region — the header/separator plus the already-completed data rows (the in-flight last row is dropped upstream by StreamingMarkdown#table_rows_so_far). The same fitted, width-clamped, border-correct path the committed table uses (block_lines -> table_lines -> balanced_column_widths), so the partial never mid-cell soft-wraps and matches the final snap.
The live region is bounded (it must never push the prompt off-screen): max_rows caps the visible DATA rows. When the table-so-far is taller, only the header + the LAST max_rows data rows render (the user watches the bottom of the table fill in), with the full table snapping in on completion via the committed path. Returns [] until a separator row has arrived (nothing meaningful to draw yet — “hide until it means something”).
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/rubino/ui/markdown_renderer.rb', line 74 def render_partial_table(lines, max_rows: nil) rows = Array(lines) sep_idx = rows.index { |l| l.to_s.match?(TABLE_SEP_RE) } return [] if sep_idx.nil? head = rows[0..sep_idx] # header row(s) + separator data = rows[(sep_idx + 1)..] || [] # No completed data row yet: kramdown won't parse a header+separator with # an empty body AS a table (it degrades to raw `| h | h |` text + an # em-dash separator), which is the very raw-pipe leak we're killing. Draw # nothing until the first data row arrives — "hide until it means # something"; the row in flight shows the moment it completes. return [] if data.empty? data = data.last(max_rows) if max_rows && data.size > max_rows render([*head, *data].join("\n")) end |