beamlet 0.1.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/README.md +0 -0
- data/bin/beamlet +5 -0
- data/lib/beamlet/cli.rb +95 -0
- data/lib/beamlet/generator.rb +143 -0
- data/lib/beamlet/templates.rb +83 -0
- data/lib/beamlet/version.rb +5 -0
- data/lib/beamlet.rb +9 -0
- metadata +47 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 266bc21197b4454a0bf0c7c347e4a6649bd070d46362f031f9b283a245887128
|
|
4
|
+
data.tar.gz: c51fdd34c879e63a8e0d67f8cf5430eb9e7a554cd1ad1c4f2279822be6475203
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a268426e495feea790f8912f3c3fac560ffda4ea51071ffc3c155efb66a7c218a8795acddea6203d142eb9cb9eedc3875916bf899d828197783842cbd34f8a03
|
|
7
|
+
data.tar.gz: 1df6b58f65ab93ef981429a6963af15647e168507bdbb1e7b8467343607815f1a6254945d92a98eaeb03c8b6573725bdf1fbf869d3e69736813664007f536875
|
data/README.md
ADDED
|
File without changes
|
data/bin/beamlet
ADDED
data/lib/beamlet/cli.rb
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
|
|
5
|
+
module Beamlet
|
|
6
|
+
class CLI
|
|
7
|
+
def self.run(argv)
|
|
8
|
+
opts = parse(argv)
|
|
9
|
+
gen = Generator.new(opts)
|
|
10
|
+
|
|
11
|
+
out = case opts[:mode]
|
|
12
|
+
when :document then gen.document
|
|
13
|
+
when :frame then gen.frame
|
|
14
|
+
when :part then gen.part
|
|
15
|
+
else
|
|
16
|
+
warn "Rien à générer. Essayez beamlet --help"
|
|
17
|
+
return 1
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
puts out
|
|
21
|
+
0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.parse(argv)
|
|
25
|
+
opts = { mode: nil, items: [], enum_items: [] }
|
|
26
|
+
|
|
27
|
+
parser = OptionParser.new do |o|
|
|
28
|
+
o.banner = "Usage: beamlet [options]"
|
|
29
|
+
|
|
30
|
+
o.on("-d", "--document", "Generate top-level document") do
|
|
31
|
+
opts[:mode] = :document
|
|
32
|
+
end
|
|
33
|
+
o.on("-f", "--frame", "Generate a frame") do
|
|
34
|
+
opts[:mode] = :frame
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
o.on("-T", "--title TITLE", "Document title") { |v| opts[:title] = v }
|
|
38
|
+
o.on("-a", "--author AUTHOR", "Author") { |v| opts[:author] = v }
|
|
39
|
+
o.on("-i", "--date DATE", "Date") { |v| opts[:date] = v }
|
|
40
|
+
o.on("--theme THEME", "Beamer theme (default Madrid)") { |v| opts[:theme] = v }
|
|
41
|
+
o.on("--[no-]titleframe", "Include title frame") { |v| opts[:titleframe] = v }
|
|
42
|
+
o.on("-P", "--part", "Generate a part slide") do
|
|
43
|
+
opts[:mode] = :part
|
|
44
|
+
end
|
|
45
|
+
o.on("-p", "--part-title TITLE", "Part title") { |v| opts[:part_title] = v }
|
|
46
|
+
o.on("-t", "--frame-title TITLE", "Frame title") { |v| opts[:frame_title] = v }
|
|
47
|
+
o.on("-s", "--splits N", Integer, "Split frame vertically into N columns") do |v|
|
|
48
|
+
opts[:splits] = v
|
|
49
|
+
end
|
|
50
|
+
o.on("--fragile", "Mark frame as fragile (for verbatim/code)") { opts[:fragile] = true }
|
|
51
|
+
o.on("--allowframebreaks", "Allow frame breaks") { opts[:allowframebreaks] = true }
|
|
52
|
+
|
|
53
|
+
o.on("-l", "--itemize A,B,C", Array, "Itemize list (left by default)") do |v|
|
|
54
|
+
opts[:items] = v
|
|
55
|
+
end
|
|
56
|
+
o.on("-e", "--enumerate A,B,C", Array, "Enumerate list") do |v|
|
|
57
|
+
opts[:enum_items] = v
|
|
58
|
+
end
|
|
59
|
+
o.on("-b", "--block TITLE", "Add a standard block") do |v|
|
|
60
|
+
opts[:block_kind] = "block"
|
|
61
|
+
opts[:block_title] = v
|
|
62
|
+
opts[:block_body] = STDIN.tty? ? "contenu..." : $stdin.read.chomp
|
|
63
|
+
end
|
|
64
|
+
o.on("-A", "--alert TITLE", "Add an alertblock") do |v|
|
|
65
|
+
opts[:block_kind] = "alertblock"
|
|
66
|
+
opts[:block_title] = v
|
|
67
|
+
opts[:block_body] = STDIN.tty? ? "attention !" : $stdin.read.chomp
|
|
68
|
+
end
|
|
69
|
+
o.on("-E", "--example TITLE", "Add an exampleblock") do |v|
|
|
70
|
+
opts[:block_kind] = "exampleblock"
|
|
71
|
+
opts[:block_title] = v
|
|
72
|
+
opts[:block_body] = STDIN.tty? ? "exemple..." : $stdin.read.chomp
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
o.on("-I", "--image FILE", "Insert image") { |v| opts[:image_file] = v }
|
|
76
|
+
o.on("--image-opts OPTS", "Image options (default width=0.8\\textwidth)") do |v|
|
|
77
|
+
opts[:image_opts] = v
|
|
78
|
+
end
|
|
79
|
+
o.on("-c", "--code LANG", "Code listing (body from stdin)") do |v|
|
|
80
|
+
opts[:code_lang] = v
|
|
81
|
+
opts[:code_body] = STDIN.tty? ? "# code" : $stdin.read.chomp
|
|
82
|
+
opts[:fragile] = true
|
|
83
|
+
end
|
|
84
|
+
o.on("-p", "--pause", "Insert \\pause") { opts[:pause] = true }
|
|
85
|
+
o.on("-n", "--note TEXT", "Speaker note") { |v| opts[:note_body] = v }
|
|
86
|
+
|
|
87
|
+
o.on("-h", "--help", "Show help") { puts o; exit 0 }
|
|
88
|
+
o.on("-v", "--version", "Show version") { puts Beamlet::VERSION; exit 0 }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
parser.parse!(argv)
|
|
92
|
+
opts
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Beamlet
|
|
4
|
+
class Generator
|
|
5
|
+
def initialize(opts = {})
|
|
6
|
+
@opts = default_opts.merge(opts)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def document
|
|
10
|
+
titleframe = @opts[:titleframe] ? Templates::TITLE_FRAME : ""
|
|
11
|
+
Templates::DOCUMENT % {
|
|
12
|
+
theme: @opts[:theme],
|
|
13
|
+
title: escape(@opts[:title]),
|
|
14
|
+
author: escape(@opts[:author]),
|
|
15
|
+
date: escape(@opts[:date]),
|
|
16
|
+
titleframe: titleframe,
|
|
17
|
+
body: "% --- vos frames ici ---"
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def part
|
|
22
|
+
Templates::PART % {
|
|
23
|
+
title: escape(@opts[:part_title] || "Partie")
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def frame
|
|
28
|
+
body = build_body
|
|
29
|
+
body = split_into_columns(body, @opts[:splits]) if @opts[:splits] && @opts[:splits] > 1
|
|
30
|
+
|
|
31
|
+
options = []
|
|
32
|
+
options << "fragile" if @opts[:fragile]
|
|
33
|
+
options << "allowframebreaks" if @opts[:allowframebreaks]
|
|
34
|
+
opt_str = options.empty? ? "" : "[#{options.join(',')}]"
|
|
35
|
+
|
|
36
|
+
Templates::FRAME % {
|
|
37
|
+
options: opt_str,
|
|
38
|
+
title: escape(@opts[:frame_title] || ""),
|
|
39
|
+
body: indent(body, 2)
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def default_opts
|
|
46
|
+
{
|
|
47
|
+
theme: "Madrid",
|
|
48
|
+
title: "Titre",
|
|
49
|
+
author: "Auteur",
|
|
50
|
+
date: "\\today",
|
|
51
|
+
titleframe: true,
|
|
52
|
+
splits: 1,
|
|
53
|
+
items: [],
|
|
54
|
+
enum_items: [],
|
|
55
|
+
block_kind: "block",
|
|
56
|
+
block_title: "",
|
|
57
|
+
block_body: "",
|
|
58
|
+
note_body: "",
|
|
59
|
+
image_file: "",
|
|
60
|
+
image_opts: "width=0.8\\textwidth",
|
|
61
|
+
code_lang: "ruby",
|
|
62
|
+
code_body: "",
|
|
63
|
+
fragile: false,
|
|
64
|
+
allowframebreaks: false,
|
|
65
|
+
pause: false
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def build_body
|
|
70
|
+
parts = []
|
|
71
|
+
|
|
72
|
+
unless @opts[:items].empty?
|
|
73
|
+
parts << format_list(Templates::ITEMIZE, @opts[:items])
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
unless @opts[:enum_items].empty?
|
|
77
|
+
parts << format_list(Templates::ENUMERATE, @opts[:enum_items])
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
unless @opts[:block_body].empty?
|
|
81
|
+
parts << Templates::BLOCK % {
|
|
82
|
+
kind: @opts[:block_kind],
|
|
83
|
+
title: escape(@opts[:block_title]),
|
|
84
|
+
body: @opts[:block_body]
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
unless @opts[:image_file].empty?
|
|
89
|
+
parts << Templates::IMAGE % {
|
|
90
|
+
opts: @opts[:image_opts],
|
|
91
|
+
file: @opts[:image_file]
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
unless @opts[:code_body].empty?
|
|
96
|
+
parts << Templates::CODE % {
|
|
97
|
+
lang: @opts[:code_lang],
|
|
98
|
+
body: @opts[:code_body]
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
parts << Templates::PAUSE if @opts[:pause]
|
|
103
|
+
|
|
104
|
+
unless @opts[:note_body].empty?
|
|
105
|
+
parts << Templates::NOTE % { body: @opts[:note_body] }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
parts.empty? ? "" : parts.join("\n\n")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def split_into_columns(body, n)
|
|
112
|
+
slices = body.split(/\n\n+/)
|
|
113
|
+
per_col = (slices.size.to_f / n).ceil
|
|
114
|
+
groups = slices.each_slice(per_col).to_a
|
|
115
|
+
groups << [] while groups.size < n
|
|
116
|
+
|
|
117
|
+
width = (1.0 / n).round(2)
|
|
118
|
+
out = [Templates::COLUMNS_OPEN]
|
|
119
|
+
groups.each do |g|
|
|
120
|
+
out << " " + Templates::COLUMN % { width: width }
|
|
121
|
+
g.each { |line| out << " " + line }
|
|
122
|
+
end
|
|
123
|
+
out << Templates::COLUMNS_CLOSE
|
|
124
|
+
out.join("\n")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def format_list(template, items)
|
|
128
|
+
rendered = items.map { |i| escape(i) }.join("\n \\item ")
|
|
129
|
+
template % { items: rendered }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def indent(str, n)
|
|
133
|
+
pad = " " * n
|
|
134
|
+
str.lines.map { |l| l.chomp.empty? ? l : pad + l }.join
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def escape(s)
|
|
138
|
+
s.to_s.gsub("\\", '\textbackslash{}')
|
|
139
|
+
.gsub(/[&%$#_{}]/) { |m| "\\#{m}" }
|
|
140
|
+
.gsub("~", '\textasciitilde{}')
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Beamlet
|
|
4
|
+
module Templates
|
|
5
|
+
DOCUMENT = <<~'TEX'
|
|
6
|
+
\documentclass[10pt,aspectratio=169]{beamer}
|
|
7
|
+
\usetheme{%{theme}}
|
|
8
|
+
\useoutertheme{infolines} % Alternatively: infolines, miniframes, shadow, sidebar, smoothbars, smoothtree, split, tree
|
|
9
|
+
\usepackage[french]{babel}
|
|
10
|
+
\usepackage[utf8]{inputenc}
|
|
11
|
+
\usepackage[T1]{fontenc}
|
|
12
|
+
\usepackage{listings}
|
|
13
|
+
\usepackage{graphicx}
|
|
14
|
+
|
|
15
|
+
\title{%{title}}
|
|
16
|
+
\author{%{author}}
|
|
17
|
+
\date{%{date}}
|
|
18
|
+
|
|
19
|
+
\begin{document}
|
|
20
|
+
%{titleframe}
|
|
21
|
+
%{body}
|
|
22
|
+
\end{document}
|
|
23
|
+
TEX
|
|
24
|
+
|
|
25
|
+
PART = <<~'TEX'
|
|
26
|
+
\part{%{title}}
|
|
27
|
+
\frame{\partpage}
|
|
28
|
+
TEX
|
|
29
|
+
|
|
30
|
+
TITLE_FRAME = <<~'TEX'
|
|
31
|
+
\begin{frame}
|
|
32
|
+
\titlepage
|
|
33
|
+
\end{frame}
|
|
34
|
+
TEX
|
|
35
|
+
|
|
36
|
+
FRAME = <<~'TEX'
|
|
37
|
+
\begin{frame}%{options}
|
|
38
|
+
\frametitle{%{title}}
|
|
39
|
+
%{body}
|
|
40
|
+
\end{frame}
|
|
41
|
+
TEX
|
|
42
|
+
|
|
43
|
+
COLUMNS_OPEN = '\begin{columns}[T]'
|
|
44
|
+
COLUMNS_CLOSE = '\end{columns}'
|
|
45
|
+
COLUMN = '\column{%{width}\textwidth}'
|
|
46
|
+
|
|
47
|
+
ITEMIZE = <<~'TEX'
|
|
48
|
+
\begin{itemize}
|
|
49
|
+
\item %{items}
|
|
50
|
+
\end{itemize}
|
|
51
|
+
TEX
|
|
52
|
+
|
|
53
|
+
ENUMERATE = <<~'TEX'
|
|
54
|
+
\begin{enumerate}
|
|
55
|
+
\item %{items}
|
|
56
|
+
\end{enumerate}
|
|
57
|
+
TEX
|
|
58
|
+
|
|
59
|
+
BLOCK = <<~'TEX'
|
|
60
|
+
\begin{%{kind}}{%{title}}
|
|
61
|
+
%{body}
|
|
62
|
+
\end{%{kind}}
|
|
63
|
+
TEX
|
|
64
|
+
|
|
65
|
+
PAUSE = '\pause'
|
|
66
|
+
|
|
67
|
+
NOTE = <<~'TEX'
|
|
68
|
+
\note{%{body}}
|
|
69
|
+
TEX
|
|
70
|
+
|
|
71
|
+
IMAGE = <<~'TEX'
|
|
72
|
+
\begin{center}
|
|
73
|
+
\includegraphics[%{opts}]{%{file}}
|
|
74
|
+
\end{center}
|
|
75
|
+
TEX
|
|
76
|
+
|
|
77
|
+
CODE = <<~'TEX'
|
|
78
|
+
\begin{lstlisting}[language=%{lang}]
|
|
79
|
+
%{body}
|
|
80
|
+
\end{lstlisting}
|
|
81
|
+
TEX
|
|
82
|
+
end
|
|
83
|
+
end
|
data/lib/beamlet.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: beamlet
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jean-Christophe Le Lann
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: A small CLI tool to emit Beamer frames, documents, columns, lists and
|
|
13
|
+
blocks.
|
|
14
|
+
executables:
|
|
15
|
+
- beamlet
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- README.md
|
|
20
|
+
- bin/beamlet
|
|
21
|
+
- lib/beamlet.rb
|
|
22
|
+
- lib/beamlet/cli.rb
|
|
23
|
+
- lib/beamlet/generator.rb
|
|
24
|
+
- lib/beamlet/templates.rb
|
|
25
|
+
- lib/beamlet/version.rb
|
|
26
|
+
homepage: http://www.jcll.fr
|
|
27
|
+
licenses:
|
|
28
|
+
- MIT
|
|
29
|
+
metadata: {}
|
|
30
|
+
rdoc_options: []
|
|
31
|
+
require_paths:
|
|
32
|
+
- lib
|
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
34
|
+
requirements:
|
|
35
|
+
- - ">="
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '3.0'
|
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '0'
|
|
43
|
+
requirements: []
|
|
44
|
+
rubygems_version: 3.6.7
|
|
45
|
+
specification_version: 4
|
|
46
|
+
summary: Generate LaTeX/Beamer snippets from the CLI
|
|
47
|
+
test_files: []
|