-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathnimiSlides.nim
232 lines (205 loc) · 10.3 KB
/
nimiSlides.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
import std/[strutils, sequtils, algorithm, strformat]
import nimib
import nimib/renders
const document = """
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.2.0/reveal.min.css" integrity="sha512-vFD6wFRj2whK8/X/dMgxJHinKfGlwMYtN+yRCxvxvmOgIiMIlgrFb5iOuCoqwCID+Qcq2/gY8DpmNHcAjfHWxw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.2.0/theme/{{{slidesTheme}}}.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.2.0/plugin/highlight/monokai.min.css" integrity="sha512-z8wQkuDRFwCBfoj7KOiu1MECaRVoXx6rZQWL21x0BsVVH7JkqCp1Otf39qve6CrCycOOL5o9vgfII5Smds23rg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
<body>
<div class="reveal">
<div class="slides">
{{{ slides }}}
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.2.0/reveal.js" integrity="sha512-+Dy2HJZ3Z1DWerDhqFE7AH2HTfnbq8RC1pKOashfMwx1s01fjPUebWoHqrRedU1yFimkexmzJJRilKxjs7lz8g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.2.0/plugin/highlight/highlight.min.js" integrity="sha512-U3fPDUX5bMrn1wnYqjaK44MFA9E6MKS+zPAg9WPAGF5XhReBeDj3FGaA831CjueG+YJxYA3WaO/m33kMIoOs/A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
{{#latex}}
<script src="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.2.1/plugin/math/math.min.js" integrity="sha512-8eviRBLZHoiXLqXeMl5XurkjNEGizTI8DHbSUoGxkYFd4RslHpIYTEQmLYtWUemc5FfMYOkPDFUcQKefPLjF7A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
{{/latex}}
<script>
Reveal.initialize({
plugins: [
RevealHighlight,
{{#latex}}
RevealMath.KaTeX
{{/latex}}
]
});
</script>
</body>
</html>
"""
type
FragmentAnimation* = enum
fadeIn = "fade-in" # the default
fadeOut = "fade-out"
fadeUp = "fade-up"
fadeDown = "fade-down"
fadeLeft = "fade-left"
fadeRight = "fade-right"
fadeInThenOut = "fade-in-then-out"
fadeInThenSemiOut = "fade-in-then-semi-out"
grow = "grow"
semiFadeOut = "semi-fade-out"
shrink = "shrink"
strike = "strike"
highlightRed = "highlight-red"
highlightGreen = "highlight-green"
highlightBlue = "highlight-blue"
highlightCurrentRed = "highlight-current-red"
highlightCurrentGreen = "highlight-current-green"
highlightCurrentBlue = "highlight-current-blue"
Slide* = ref object
pos*: tuple[start: int, finish: int]
notes*: seq[string]
SlidesCtx* = ref object
sections*: seq[seq[Slide]]
SlidesTheme* = enum
Black, Beige, Blood, League, Moon, Night, Serif, Simple, Sky, Solarized, White
################################
# This is modified code from nimib.
# We need to render code blocks using the context to populate the {{ endFragment-i }} fields.
################################
proc renderHtmlTextOutput*(output: string, doc: NbDoc): string =
renderMarkdown(output.strip).render(doc.context)
proc renderHtmlBlock*(blk: NbBlock, doc: NbDoc): string =
case blk.kind
of nbkText:
result = blk.output.renderHtmlTextOutput(doc)
of nbkCode:
result = blk.code.renderHtmlCodeBody
if blk.output != "":
result.add blk.output.renderHtmlCodeOutput
of nbkImage:
let
image_url = blk.code
caption = blk.output
result = fmt"""
<figure>
<img src="{image_url}" alt="{caption}">
<figcaption>{caption}</figcaption>
</figure>
""" & "\n"
###########################
###########################
template initReveal*() =
## Call this after nbInit
var slidesCtx {.inject.} = SlidesCtx(sections: @[@[Slide(pos: (start: 0, finish: -1))]])
nb.context["currentFragment"] = 0
nb.context["currentEndFragment"] = 0
template slideRight() =
## Add a slide to the right of the current one
slidesCtx.sections[^1][^1].pos.finish = nb.blocks.len - 1
slidesCtx.sections.add @[Slide(pos: (start: nb.blocks.len, finish: -1))]
template slideDown() =
## Add a slide below the current one
slidesCtx.sections[^1][^1].pos.finish = nb.blocks.len - 1
slidesCtx.sections[^1].add (Slide(pos: (start: nb.blocks.len, finish: -1)))
template slideRight(body: untyped) =
slideRight()
body
template slideDown(body: untyped) =
slideDown()
body
template fragmentCore(animations: openArray[seq[FragmentAnimation]] = @[], endAnimations: openArray[seq[FragmentAnimation]] = @[], body: untyped) =
## Creates a fragment of the content of body. Nesting works.
## animations: each seq in animations are animations that are to be applied at the same time. The first seq's animations
## are applied on the first button click, and the second seq's animations on the second click etc.
## endAnimations: animations that should be applied AT THE END of block.
## Example:
## `fragment(@[@[fadeIn, highlightBlue], @[shrink, semiFadeOut]]): block` will at the first click of a button fadeIn and highlightBlue
## the content of the block. At the second click the same content will shrink and semiFadeOut. This code is also equivilent with
## `fragment(@[@[fadeIn, highlightBlue]]): fragment(@[@[shrink, semiFadeOut]]): block`.
## `fragment(@[@[fadeIn]], @[@[fadeOut]]): block` will first fadeIn the entire block and perform eventual animations in nested fragments. Once
## all of those are finished, it will run fadeOut on the entire block and its subfragments.
var endIds: seq[string]
for level in animations: # level are the animations to be applied simulataniously to a fragment
let classStr = join(level, " ")
let dataIndexStr = "data-fragment-index=\"" & $(nb.context["currentFragment"].vInt) & "\""
nbText: "<div class=\"fragment " & classStr & "\" " & dataIndexStr & ">"
nb.context["currentFragment"] = nb.context["currentFragment"].vInt + 1
for level in endAnimations:
let classStr = join(level, " ")
let id = "endFragment-" & $(nb.context["currentEndFragment"].vInt)
let dataIndexStr = "data-fragment-index=\"" & "{{ " & id & " }}" & "\""
nbText: "<div class=\"fragment " & classStr & "\" " & dataIndexStr & ">"
nb.context["currentEndFragment"] = nb.context["currentEndFragment"].vInt + 1
endIds.add id
body
for id in endIds:
# Add the end indices after the block:
nb.context[id] = $(nb.context["currentFragment"].vInt)
nb.context["currentFragment"] = nb.context["currentFragment"].vInt + 1
nbText: "</div>".repeat(animations.len + endAnimations.len) # add a closing tag for every level
template fragment(animations: varargs[seq[FragmentAnimation]] = @[@[fadeIn]], body: untyped): untyped =
## Creates a fragment of the content of body. Nesting works.
## animations: each seq of the varargs are animations that are to be applied at the same time. The first seq's animations
## are applied on the first button click, and the second seq's animations on the second click etc.
## Example:
## `fragment(@[fadeIn, highlightBlue], @[shrink, semiFadeOut]): block` will at the first click of a button fadeIn and highlightBlue
## the content of the block. At the second click the same content will shrink and semiFadeOut. This code is also equivilent with
## `fragment(@[fadeIn, highlightBlue]): fragment(@[shrink, semiFadeOut]): block`.
fragmentCore(@animations):
body
template fragment(animation: FragmentAnimation, body: untyped) =
## fragment(animation) is shorthand for fragment(@[animation])
fragmentCore(@[@[animation]]):
body
template fragmentFadeIn(animation: FragmentAnimation, body: untyped) =
fragmentCore(@[@[fadeIn], @[animation]]):
body
template fragmentEnd(endAnimation: varargs[seq[FragmentAnimation]], body: untyped) =
fragmentCore(endAnimations = @endAnimation):
body
template fragmentEnd(endAnimation: FragmentAnimation, body: untyped) =
fragmentCore(endAnimations = @[@[endAnimation]]):
body
template animateCode(lines: varargs[HSlice], body: untyped) =
## Shows code and its output just like nbCode, but highlights different lines of the code in the order specified in `lines`.
## lines: Specify which lines to highlight and in which order. (Must be specified as a HSlice)
## Ex:
## ```nim
## animateCode(1..1, 2..3, 5..5, 4..4): body
## ```
## This will first highlight line 1, then lines 2 and 3, then line 5 and last line 4.
discard
template bigText(text: string) =
nbText: "<h2 class=\"r-fit-text\">" & text & "</h2>"
template removeCodeOutput =
if nb.blocks.len > 0:
var blk = nb.blocks[^1]
if blk.kind == nbkCode:
blk.output = ""
template setSlidesTheme(theme: SlidesTheme) =
nb.context["slidesTheme"] = ($theme).toLower
proc renderSlide(doc: NbDoc, slide: Slide): string =
let upper =
if slide.pos.finish != -1: slide.pos.finish
else: doc.blocks.len - 1
result = "<section>\n"
for i in slide.pos.start .. upper:
result &= doc.blocks[i].renderHtmlBlock(doc)
result &= "</section>\n"
proc renderReveal*(doc: NbDoc): string =
var content: string
for horiz in slidesCtx.sections:
content &= "<section>\n" # this is the top level section
for vertical in horiz:
# vertical corresponds to a single slide with many blocks. Must loop over them all and call `renderHTMLBlock`
# if vertical.finish == -1: it is the last slide, grab the rest of all blocks
content &= doc.renderSlide(vertical)
content &= "</section>\n"
doc.context["slides"] = content
# This is neccecary because it will show the <span> tag otherwise:
result = "{{> document}}".render(doc.context).replace("<code class=\"nim hljs\">", "<code class=\"nim hljs\" data-noescape>")
result = result.replace("<pre><samp", "<pre style=\"width: 100%;\"><samp class=\"hljs\"") # add some background to code output block
result = result.replace("<pre>", "<pre style=\"width: 100%\">") # this makes code blocks a little bit wider
nb.render = renderReveal
proc revealTheme*(doc: var NbDoc) =
doc.partials["document"] = document
doc.context["slidesTheme"] = "black"