Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
5702769a
Commit
5702769a
authored
May 18, 2017
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add slides for meetup talk.
parent
8db916de
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
932 additions
and
11 deletions
+932
-11
slides.html
doc/_layouts/slides.html
+54
-0
main.js
doc/javascripts/main.js
+59
-0
3ph-0rt.png
doc/slides-2017.05.18/3ph-0rt.png
+0
-0
3ph-proxy.png
doc/slides-2017.05.18/3ph-proxy.png
+0
-0
3ph-redirect.png
doc/slides-2017.05.18/3ph-redirect.png
+0
-0
3ph.png
doc/slides-2017.05.18/3ph.png
+0
-0
index.md
doc/slides-2017.05.18/index.md
+664
-0
pygment_trac.css
doc/stylesheets/pygment_trac.css
+11
-11
stylesheet.css
doc/stylesheets/stylesheet.css
+144
-0
No files found.
doc/_layouts/slides.html
0 → 100644
View file @
5702769a
<!DOCTYPE html>
<html>
<head>
<meta
charset=
'utf-8'
/>
<meta
http-equiv=
"X-UA-Compatible"
content=
"chrome=1"
/>
<meta
name=
"viewport"
content=
"width=480"
>
<link
rel=
"stylesheet"
type=
"text/css"
media=
"screen"
href=
"{{ site.baseurl }}stylesheets/stylesheet.css"
>
<link
rel=
"alternate"
type=
"application/rss+xml"
title=
"Cap'n Proto News"
href=
"{{site.baseurl}}feed.xml"
>
<title>
Cap'n Proto: {{ page.title }}
</title>
<script
type=
"text/javascript"
src=
"{{ site.baseurl }}javascripts/main.js"
></script>
</head>
<body
class=
"slides"
>
<header>
<img
src=
"{{ site.baseurl }}images/logo.png"
>
<h1
class=
"title"
></h1>
</header>
<!-- MAIN CONTENT -->
<main>
{{ content }}
</main>
<!-- FOOTER -->
<footer>
<p
class=
"url"
>
<button
class=
"back"
>
◀
</button>
<button
class=
"forward"
>
▶
</button>
https://capnproto.org{{ page.url }}
<span
id=
"slide-num"
></span>
</p>
<h2>
Kenton Varda
</h2>
<p>
May 18, 2017
</p>
</footer>
<script
type=
"text/javascript"
>
setupSlides
();
</script>
<!-- Google Analytics. -->
<script
type=
"text/javascript"
>
var
gaJsHost
=
((
"https:"
==
document
.
location
.
protocol
)
?
"https://ssl."
:
"http://www."
);
document
.
write
(
unescape
(
"%3Cscript src='"
+
gaJsHost
+
"google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"
));
</script>
<script
type=
"text/javascript"
>
try
{
var
pageTracker
=
_gat
.
_getTracker
(
"UA-39711112-1"
);
pageTracker
.
_trackPageview
();
}
catch
(
err
)
{}
</script>
</body>
</html>
doc/javascripts/main.js
View file @
5702769a
...
...
@@ -139,3 +139,62 @@ function setupNewsSidebar(items) {
}
}
}
function
setupSlides
()
{
var
slides
=
document
.
querySelectorAll
(
"body.slides main section"
);
var
headerTitle
=
document
.
querySelector
(
"body.slides header .title"
);
var
slideNum
=
document
.
querySelector
(
"#slide-num"
);
var
current
=
0
;
var
hash
=
document
.
location
.
hash
;
if
(
hash
)
{
current
=
parseInt
(
hash
.
slice
(
1
))
-
1
;
}
slides
[
current
].
className
=
"current"
;
headerTitle
.
textContent
=
slides
[
current
].
dataset
.
title
||
""
;
slideNum
.
textContent
=
window
.
location
.
hash
;
function
navSlide
(
diff
)
{
slides
[
current
].
className
=
""
;
current
=
Math
.
min
(
slides
.
length
-
1
,
Math
.
max
(
0
,
current
+
diff
));
slides
[
current
].
className
=
"current"
;
headerTitle
.
textContent
=
slides
[
current
].
dataset
.
title
||
""
;
if
(
current
)
{
history
.
replaceState
({},
""
,
"#"
+
(
current
+
1
));
slideNum
.
textContent
=
"#"
+
(
current
+
1
);
}
else
{
history
.
replaceState
({},
""
,
window
.
location
.
pathname
);
slideNum
.
textContent
=
""
;
}
}
document
.
body
.
addEventListener
(
"keydown"
,
event
=>
{
if
(
event
.
keyCode
==
39
)
{
navSlide
(
1
);
}
else
if
(
event
.
keyCode
==
37
)
{
navSlide
(
-
1
);
}
});
document
.
querySelector
(
"body.slides footer button.back"
).
addEventListener
(
"click"
,
event
=>
{
navSlide
(
-
1
);
});
document
.
querySelector
(
"body.slides footer button.forward"
).
addEventListener
(
"click"
,
event
=>
{
navSlide
(
1
);
});
if
(
document
.
location
.
hostname
===
"localhost"
)
{
var
lastModified
=
new
Date
(
document
.
lastModified
);
setInterval
(
function
()
{
var
req
=
new
Request
(
"."
,
{
headers
:
{
"If-Modified-Since"
:
lastModified
.
toUTCString
()}});
fetch
(
req
).
then
(
response
=>
{
if
(
response
.
status
==
200
&&
new
Date
(
response
.
headers
.
get
(
"Last-Modified"
))
>
lastModified
)
{
document
.
location
.
reload
();
}
});
},
1000
);
}
}
doc/slides-2017.05.18/3ph-0rt.png
0 → 100644
View file @
5702769a
152 KB
doc/slides-2017.05.18/3ph-proxy.png
0 → 100644
View file @
5702769a
166 KB
doc/slides-2017.05.18/3ph-redirect.png
0 → 100644
View file @
5702769a
152 KB
doc/slides-2017.05.18/3ph.png
0 → 100644
View file @
5702769a
169 KB
doc/slides-2017.05.18/index.md
0 → 100644
View file @
5702769a
---
layout
:
slides
title
:
"
Slides:
What's
Next
for
Cap'n
Proto"
---
<!--===================================================================================-->
<section
markdown=
"1"
id=
"slides-cover"
>
What's Next for Cap'n Proto?
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Streaming"
>
Cap'n Proto supports streaming!
{% highlight capnp %}
interface FileStore {
get @0 (name :Text, stream :Stream);
put @1 (name :Text) -> (stream :Stream);
}
interface Stream {
write @0 (data :Data);
end @1 ();
}
{% endhighlight %}
But flow control is up to the app.
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Flow Control"
>
Let's build it in.
{% highlight capnp %}
interface Stream {
write @0 (data :Data) -> bulk;
end @1 ();
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Realtime"
>
What about realtime streams?
{% highlight capnp %}
interface VideoCallStream {
sendFrame @0 (frame :Frame) -> realtime;
}
{% endhighlight %}
<br>
Best served on a UDP transport...
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Three-Party Handoff"
>
<img
class=
"ph3"
src=
"3ph.png"
>
Forwarded request.
Where does response go?
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Three-Party Handoff"
>
<img
class=
"ph3"
src=
"3ph-proxy.png"
>
Classic solution:
Proxy
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Three-Party Handoff"
>
<img
class=
"ph3"
src=
"3ph-redirect.png"
>
Classic solution:
Redirect
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Three-Party Handoff"
>
<img
class=
"ph3"
src=
"3ph-0rt.png"
>
Cap'n Proto:
3-Party Handoff
(aka 3PH)
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Three-Party Handoff"
>
<img
class=
"ph3"
src=
"3ph-0rt.png"
>
Cap'n Proto:
3-Party Handoff
(aka 3PH)
... gonna need UDP
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Three-Party Handoff"
>
<img
class=
"ph3"
src=
"3ph-0rt.png"
>
Cap'n Proto:
3-Party Handoff
(aka 3PH)
... gonna need UDP
... and 0-RT crypto
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Three-Party Handoff"
>
API: "Tail call"
{% highlight c++ %}
kj::Promise
<void>
myRpc(MyRpcContext context) override {
// Begin sub-request.
auto subRequest = someCapability.someRpcRequest();
subRequest.setSomeParam(someValue);
// Send as a tail call.
return context.tailCall(kj::mv(subRequest));
}
{% endhighlight %}
Today: Will proxy
<br>
Future: 3PH
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"KJ TLS Bindings"
>
KJ client networking, no TLS:
{% highlight c++ %}
void send() {
auto io = kj::setupAsyncIo();
auto& network = io.provider->getNetwork();
auto addr = network.parseAddress("capnproto.org", 80)
.wait(io.waitScope);
auto connection = addr->connect().wait(io.waitScope);
connection->write("GET /", 5).wait(io.waitScope);
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"KJ TLS Bindings"
>
KJ client networking with TLS:
{% highlight c++ %}
void send() {
auto io = kj::setupAsyncIo();
kj::TlsContext tls;
auto network = tls.wrapNetwork(io.provider->getNetwork());
auto addr = network->parseAddress("capnproto.org", 443)
.wait(io.waitScope);
auto connection = addr->connect().wait(io.waitScope);
connection->write("GET /", 5).wait(io.waitScope);
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"KJ TLS Bindings"
>
Diff:
{% highlight c++ %}
void send() {
kj::TlsContext tls;
tls.wrapNetwork( );
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"KJ TLS Bindings"
>
{% highlight c++ %}
void receive() {
auto io = kj::setupAsyncIo();
auto& network = io.provider->getNetwork();
auto addr = network.parseAddress("
*
", 80)
.wait(io.waitScope);
auto listener = addr->listen();
auto connection = listener->accept().wait(io.waitScope);
connection->write("HTTP/1.1 404 Not Found
\r\n\r\n
", 26)
.wait(io.waitScope);
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"KJ TLS Bindings"
>
{% highlight c++ %}
void receive() {
auto io = kj::setupAsyncIo();
kj::TlsKeypair keypair { KEY_PEM_TEXT, CERT_PEM_TEXT };
kj::TlsContext::Options options;
options.defaultKeypair = keypair;
kj::TlsContext tls(options);
auto& network = io.provider->getNetwork();
auto addr = network.parseAddress("
*
", 443).wait(io.waitScope);
auto listener = tls.wrapPort(addr->listen());
auto connection = listener->accept().wait(io.waitScope);
connection->write("HTTP/1.1 404 Not Found
\r\n\r\n
", 26)
.wait(io.waitScope);
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"KJ TLS Bindings"
>
{% highlight c++ %}
void receive() {
kj::TlsKeypair keypair { KEY_PEM_TEXT, CERT_PEM_TEXT };
kj::TlsContext::Options options;
options.defaultKeypair = keypair;
kj::TlsContext tls(options);
tls.wrapPort( );
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"KJ HTTP Library"
>
{% highlight c++ %}
auto io = kj::setupAsyncIo();
kj::HttpHeaderTable headerTable;
auto client = kj::newHttpClient(
*
headerTable, io.provider->getNetwork());
kj::HttpHeaders headers(
*
headerTable);
auto response = client->request(
kj::HttpMethod::GET, "http://capnproto.org", headers)
.response.wait(io.waitScope);
KJ_ASSERT(response.statusCode == 200);
KJ_LOG(INFO, response.body->readAllText().wait(io.waitScope));
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"KJ HTTP Library"
>
Headers identified by small numbers.
{% highlight c++ %}
kj::HttpHeaderTable::Builder builder;
kj::HttpHeaderId userAgent = builder.add("User-Agent");
auto headerTable = builder.build();
kj::HttpHeaders headers(
*
headerTable);
headers.set(kj::HttpHeaderId::HOST, "capnproto.org");
headers.set(userAgent, "kj-http/0.6");
{% endhighlight %}
Header parsing is zero-copy.
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Designated Initializers"
>
Old and busted:
{% highlight c++ %}
capnp::MallocMessageBuilder message;
auto root = message.initRoot
<MyStruct>
();
root.setInt32Field(123);
root.setTextField("foo");
auto inner = root.initStructField();
inner.setBoolField(true);
capnp::writeMessageToFd(fd, message);
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Designated Initializers"
>
New hotness:
{% highlight c++ %}
using namespace capnp::init;
capnp::MallocMessageBuilder message;
message.initRoot
<MyStruct>
(
$int32Field = 123,
$textField = "foo",
$structField(
$boolField = true
)
);
capnp::writeMessageToFd(fd, message);
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Designated Initializers"
>
Even better:
{% highlight c++ %}
using namespace capnp::init;
capnp::writeMessageToFd
<MyStruct>
(fd,
$int32Field = 123,
$textField = "foo",
$structField(
$boolField = true
)
);
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Designated Initializers"
>
{% highlight c++ %}
struct {
template
<typename
T
>
struct Setter {
T value;
template
<typename
U
>
void operator()(U& target) {
target.setInt32Field(kj::fwd
<T>
(value));
}
};
template
<typename
T
>
Setter
<T>
operator=(T&& value) {
return { kj::fwd
<T>
(value) };
}
} $int32Field;
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"POCS"
>
Kind of painful:
{% highlight c++ %}
capnp::MallocMessageBuilder message;
MyStruct::Builder root = message.initRoot
<MyStruct>
();
root.setInt32Field(123);
root.setTextField("foo");
InnerStruct::Builder inner = root.initStructField();
inner.setBoolField(true);
capnp::writeMessageToFd(fd, message);
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"POCS"
>
Plain Old C++ Structs?
{% highlight c++ %}
MyStruct root;
root.int32Field = 123;
root.textField = "foo";
InnerStruct inner;
inner.boolField = true;
root.structField = kj::mv(inner);
capnp::writeMessageToFd(fd, message);
{% endhighlight %}
Caveat: No longer zero-copy.
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"POCS"
>
{% highlight c++ %}
capnp::MallocMessageBuilder message;
capnp::readMessageCopy(input, message);
auto root = message.getRoot
<MyStruct>
();
auto oldListOrphan = root.disownStructList();
auto oldList = oldListOrphan.getReader();
auto newList = root.initStructList(oldList.size() - 1);
for (auto i: kj::indices(newList)) {
newList.setWithCaveats(i,
oldList
[
i < indexToRemove ? i : i + 1
]
);
}
capnp::MallocMessageBuilder message2;
message2.setRoot(root.asReader());
capnp::writeMessage(output, message2);
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"POCS"
>
{% highlight c++ %}
auto root = capnp::readMessageCopy
<MyStruct>
(input);
root.structList.erase(indexToRemove);
capnp::writeMessageCopy(output, root);
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"Threads"
>
Cap'n Proto RPC between threads!
Actor model!
... that's all I have to say about that.
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"JSON-HTTP Bridge"
>
{% highlight capnp %}
interface AddressBook {
getPerson @0 (id :UInt32 $httpPath)
-> (person :Person $httpBody(type = json))
$http(method = get, route = "person");
# GET /person/
<id>
# JSON response body
updatePerson @1 (id :UInt32 $httpPath,
person :Person $httpBody(type = json));
$http(method = put, route = "person");
# PUT /person/
<id>
# JSON request body
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"JSON-HTTP Bridge"
>
{% highlight capnp %}
addPerson @2 (person :Person $httpBody(type = json))
-> (id :UInt32 $httpBody(type = jsonField));
$http(method = post, route = "person");
# POST /person
# JSON request body
# JSON response body (object containing field `id`)
getAll @3 (page :UInt32 = 0 $httpQuery)
-> (people: List(Person) $httpBody(type = json));
$http(method = get);
# GET /?page=<num>
# Query is optional.
# JSAN (JSON array) repsonse body.
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
data-title=
"JSON-HTTP Bridge"
>
{% highlight capnp %}
interface AddressBookService {
getAddressBook @0 (key :String $httpPath)
-> (result :AddressBook $httpPipeline);
$http(route = "book");
# GET /book/JrpmUduyHd8uW3x3TOXn2g/person/123
# Becomes:
# service.getAddressBook("JrpmUduyHd8uW3x3TOXn2g").send()
# .getResult().getPerson(123).send()
#
# GET /book/JrpmUduyHd8uW3x3TOXn2g
# Becomes:
# service.getAddressBook("JrpmUduyHd8uW3x3TOXn2g").send()
# .getResult().getAll().send()
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section
markdown=
"1"
id=
"slides-cover"
>
Questions?
</section>
doc/stylesheets/pygment_trac.css
View file @
5702769a
...
...
@@ -23,29 +23,29 @@
.highlight
.kn
{
color
:
#006699
;
font-weight
:
bold
}
/* Keyword.Namespace */
.highlight
.kp
{
color
:
#006699
}
/* Keyword.Pseudo */
.highlight
.kr
{
color
:
#006699
;
font-weight
:
bold
}
/* Keyword.Reserved */
.highlight
.kt
{
color
:
#00
7788
;
font-weight
:
bold
}
/* Keyword.Type */
.highlight
.m
{
color
:
#
FF6600
}
/* Literal.Number */
.highlight
.s
{
color
:
#
CC3300
}
/* Literal.String */
.highlight
.kt
{
color
:
#00
9900
;
font-weight
:
bold
}
/* Keyword.Type */
.highlight
.m
{
color
:
#
0000FF
}
/* Literal.Number */
.highlight
.s
{
color
:
#
0000FF
}
/* Literal.String */
.highlight
.na
{
color
:
#330099
}
/* Name.Attribute */
.highlight
.nb
{
color
:
#
336666
}
/* Name.Builtin */
.highlight
.nb
{
color
:
#
0000FF
}
/* Name.Builtin */
.highlight
.nc
{
color
:
#00AA88
;
font-weight
:
bold
}
/* Name.Class */
.highlight
.no
{
color
:
#336600
}
/* Name.Constant */
.highlight
.nd
{
color
:
#9999FF
}
/* Name.Decorator */
.highlight
.ni
{
color
:
#999999
;
font-weight
:
bold
}
/* Name.Entity */
.highlight
.ne
{
color
:
#CC0000
;
font-weight
:
bold
}
/* Name.Exception */
.highlight
.nf
{
color
:
#CC00FF
}
/* Name.Function */
.highlight
.nl
{
color
:
#9999FF
}
/* Name.Label */
.highlight
.nf
{
color
:
inherit
}
/* Name.Function */
.highlight
.nl
{
color
:
inherit
}
/* Name.Label */
.highlight
.nn
{
color
:
#00CCFF
;
font-weight
:
bold
}
/* Name.Namespace */
.highlight
.nt
{
color
:
#330099
;
font-weight
:
bold
}
/* Name.Tag */
.highlight
.nv
{
color
:
#003333
}
/* Name.Variable */
.highlight
.ow
{
color
:
#000000
;
font-weight
:
bold
}
/* Operator.Word */
.highlight
.w
{
color
:
#bbbbbb
}
/* Text.Whitespace */
.highlight
.mf
{
color
:
#
FF6600
}
/* Literal.Number.Float */
.highlight
.mh
{
color
:
#
FF6600
}
/* Literal.Number.Hex */
.highlight
.mi
{
color
:
#
FF6600
}
/* Literal.Number.Integer */
.highlight
.mo
{
color
:
#
FF6600
}
/* Literal.Number.Oct */
.highlight
.mf
{
color
:
#
0000FF
}
/* Literal.Number.Float */
.highlight
.mh
{
color
:
#
0000FF
}
/* Literal.Number.Hex */
.highlight
.mi
{
color
:
#
0000FF
}
/* Literal.Number.Integer */
.highlight
.mo
{
color
:
#
0000FF
}
/* Literal.Number.Oct */
.highlight
.sb
{
color
:
#CC3300
}
/* Literal.String.Backtick */
.highlight
.sc
{
color
:
#
CC3300
}
/* Literal.String.Char */
.highlight
.sc
{
color
:
#
0000FF
}
/* Literal.String.Char */
.highlight
.sd
{
color
:
#CC3300
;
font-style
:
italic
}
/* Literal.String.Doc */
.highlight
.s2
{
color
:
#CC3300
}
/* Literal.String.Double */
.highlight
.se
{
color
:
#CC3300
;
font-weight
:
bold
}
/* Literal.String.Escape */
...
...
doc/stylesheets/stylesheet.css
View file @
5702769a
...
...
@@ -709,3 +709,147 @@ table.pass-fail td.pass { background-color: #8f8; }
table
.pass-fail
td
.fail
{
background-color
:
#f88
;
}
table
.pass-fail
td
.warn
{
background-color
:
#ff8
;
}
/*******************************************************************************
Slides
*******************************************************************************/
body
.slides
*
{
box-sizing
:
border-box
;
}
body
.slides
header
{
background
:
#C42727
;
position
:
fixed
;
top
:
0
;
left
:
0
;
width
:
100%
;
height
:
20%
;
}
body
.slides
header
>
img
{
float
:
right
;
height
:
20vh
;
padding
:
2vh
;
width
:
auto
;
}
body
.slides
header
>
.title
{
color
:
white
;
font-size
:
8vh
;
text-align
:
left
;
line-height
:
20vh
;
padding-left
:
5vh
;
}
body
.slides
main
{
font-size
:
4vh
;
}
body
.slides
main
section
{
border-top
:
1px
solid
#111
;
border-bottom
:
1px
solid
#111
;
position
:
fixed
;
top
:
20%
;
left
:
0
;
width
:
100%
;
bottom
:
10%
;
background-color
:
#f2f2f2
;
position
:
absolute
;
z-index
:
-1
;
padding
:
5vh
;
}
body
.slides
main
section
.current
{
z-index
:
1
;
}
body
.slides
main
section
#slides-cover
{
background
:
url(/images/logo.png)
center
/
75%
no-repeat
#C42727
;
top
:
0
;
border-top
:
none
;
}
body
.slides
main
section
#slides-cover
p
{
color
:
white
;
position
:
absolute
;
bottom
:
0
;
left
:
0
;
right
:
0
;
text-align
:
center
;
font-size
:
6vh
;
font-weight
:
bold
;
}
body
.slides
main
img
.ph3
{
background-color
:
white
;
padding
:
4vh
;
height
:
60vh
;
width
:
auto
;
box-shadow
:
0
0
10px
rgba
(
0
,
0
,
0
,
.1
);
float
:
left
;
margin-right
:
2vh
;
}
body
.slides
pre
{
margin
:
0
0
2em
;
}
body
.slides
pre
code
{
font-size
:
3vh
;
}
body
.slides
footer
{
background
:
#212121
;
position
:
fixed
;
width
:
100%
;
height
:
10%
;
left
:
0
;
bottom
:
0
;
color
:
white
;
text-align
:
right
;
padding
:
2vh
;
}
body
.slides
footer
h2
{
font-size
:
4vh
;
height
:
4vh
;
margin
:
0
;
padding
:
0
;
background
:
none
;
color
:
inherit
;
line-height
:
4vh
;
}
body
.slides
footer
p
{
font-size
:
2vh
;
height
:
2vh
;
line-height
:
2vh
;
margin
:
0
;
}
body
.slides
footer
p
.url
{
position
:
absolute
;
left
:
5vh
;
top
:
0
;
bottom
:
0
;
font-size
:
4vh
;
line-height
:
10vh
;
margin
:
0
;
}
body
.slides
footer
button
{
width
:
5vh
;
height
:
5vh
;
font-size
:
4vh
;
padding
:
0
0
0.5vh
0
;
border
:
none
;
border-radius
:
0.5vh
;
background-color
:
white
;
}
body
.slides
footer
button
:last-of-type
{
margin-right
:
1vh
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment