Use embedded SVG instead of fonts for icons

The [downsides of icon fonts] are well-documented, and also, why ship all of
the icons when it only uses 14?

[downsides of icon fonts]: https://speakerdeck.com/ninjanails/death-to-icon-fonts
This commit is contained in:
Michael Howell
2024-06-08 14:19:48 -07:00
committed by Eric Huss
parent 7b3e6973be
commit 2dc8c5e686
24 changed files with 238 additions and 2769 deletions

7
Cargo.lock generated
View File

@@ -622,6 +622,12 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "font-awesome-as-a-crate"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "932dcfbd51320af5f27f1ba02d2e567dec332cac7d2c221ba45d8e767264c4dc"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
@@ -1352,6 +1358,7 @@ dependencies = [
"ammonia",
"anyhow",
"elasticlunr-rs",
"font-awesome-as-a-crate",
"handlebars",
"hex",
"log",

View File

@@ -33,6 +33,7 @@ clap = { version = "4.5.41", features = ["cargo", "wrap_help"] }
clap_complete = "4.5.55"
elasticlunr-rs = "3.0.2"
env_logger = "0.11.8"
font-awesome-as-a-crate = "0.3.0"
futures-util = "0.3.31"
handlebars = "6.3.2"
hex = "0.4.3"

View File

@@ -11,6 +11,7 @@ rust-version.workspace = true
ammonia = { workspace = true, optional = true }
anyhow.workspace = true
elasticlunr-rs = { workspace = true, optional = true }
font-awesome-as-a-crate.workspace = true
handlebars.workspace = true
hex.workspace = true
log.workspace = true

View File

@@ -56,7 +56,7 @@ html.sidebar-visible #menu-bar {
#menu-bar.bordered {
border-block-end-color: var(--table-border-color);
}
#menu-bar i, #menu-bar .icon-button {
#menu-bar .fa-svg, #menu-bar .icon-button {
position: relative;
padding: 0 8px;
z-index: 10;
@@ -65,7 +65,7 @@ html.sidebar-visible #menu-bar {
transition: color 0.5s;
}
@media only screen and (max-width: 420px) {
#menu-bar i, #menu-bar .icon-button {
#menu-bar .fa-svg, #menu-bar .icon-button {
padding: 0 5px;
}
}
@@ -76,7 +76,7 @@ html.sidebar-visible #menu-bar {
padding: 0;
color: inherit;
}
.icon-button i {
.icon-button .fa-svg {
margin: 0;
}
@@ -118,14 +118,14 @@ html:not(.js) .left-buttons button {
.mobile-nav-chapters,
.mobile-nav-chapters:visited,
.menu-bar .icon-button,
.menu-bar a i {
.menu-bar a .fa-svg {
color: var(--icons);
}
.menu-bar i:hover,
.menu-bar .fa-svg:hover,
.menu-bar .icon-button:hover,
.nav-chapters:hover,
.mobile-nav-chapters i:hover {
.mobile-nav-chapters .fa-svg:hover {
color: var(--icons-hover);
}
@@ -240,13 +240,10 @@ pre > .buttons :hover {
border-color: var(--icons-hover);
background-color: var(--theme-hover);
}
pre > .buttons i {
margin-inline-start: 8px;
}
pre > .buttons button {
cursor: inherit;
margin: 0px 5px;
padding: 4px 4px 3px 5px;
padding: 2px 3px 0px 4px;
font-size: 23px;
border-style: solid;
@@ -365,6 +362,20 @@ mark.fade-out {
background-color: var(--bg);
}
#fa-spin {
animation: rotating 2s linear infinite;
display: inline-block;
}
@keyframes rotating {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#searchbar {
width: 100%;
margin-block-start: var(--searchbar-margin-block-start);

File diff suppressed because one or more lines are too long

View File

@@ -278,3 +278,10 @@ sup {
.result-no-output {
font-style: italic;
}
.fa-svg svg {
width: 1em;
height: 1em;
fill: currentColor;
margin-bottom: -0.1em;
}

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 434 KiB

View File

@@ -97,6 +97,7 @@ function playground_text(playground, hidden = true) {
if (all_available) {
play_button.classList.remove('hidden');
play_button.hidden = false;
} else {
play_button.classList.add('hidden');
}
@@ -207,26 +208,25 @@ function playground_text(playground, hidden = true) {
const buttons = document.createElement('div');
buttons.className = 'buttons';
buttons.innerHTML = '<button class="fa fa-eye" title="Show hidden lines" \
buttons.innerHTML = '<button title="Show hidden lines" \
aria-label="Show hidden lines"></button>';
buttons.firstChild.innerHTML = document.getElementById('fa-eye').innerHTML;
// add expand button
const pre_block = block.parentNode;
pre_block.insertBefore(buttons, pre_block.firstChild);
pre_block.querySelector('.buttons').addEventListener('click', function(e) {
if (e.target.classList.contains('fa-eye')) {
e.target.classList.remove('fa-eye');
e.target.classList.add('fa-eye-slash');
e.target.title = 'Hide lines';
e.target.setAttribute('aria-label', e.target.title);
buttons.firstChild.addEventListener('click', function(e) {
if (this.title === 'Show hidden lines') {
this.innerHTML = document.getElementById('fa-eye-slash').innerHTML;
this.title = 'Hide lines';
this.setAttribute('aria-label', e.target.title);
block.classList.remove('hide-boring');
} else if (e.target.classList.contains('fa-eye-slash')) {
e.target.classList.remove('fa-eye-slash');
e.target.classList.add('fa-eye');
e.target.title = 'Show hidden lines';
e.target.setAttribute('aria-label', e.target.title);
} else if (this.title === 'Hide lines') {
this.innerHTML = document.getElementById('fa-eye').innerHTML;
this.title = 'Show hidden lines';
this.setAttribute('aria-label', e.target.title);
block.classList.add('hide-boring');
}
@@ -266,10 +266,11 @@ aria-label="Show hidden lines"></button>';
}
const runCodeButton = document.createElement('button');
runCodeButton.className = 'fa fa-play play-button';
runCodeButton.className = 'play-button';
runCodeButton.hidden = true;
runCodeButton.title = 'Run this code';
runCodeButton.setAttribute('aria-label', runCodeButton.title);
runCodeButton.innerHTML = document.getElementById('fa-play').innerHTML;
buttons.insertBefore(runCodeButton, buttons.firstChild);
runCodeButton.addEventListener('click', () => {
@@ -289,9 +290,11 @@ aria-label="Show hidden lines"></button>';
const code_block = pre_block.querySelector('code');
if (window.ace && code_block.classList.contains('editable')) {
const undoChangesButton = document.createElement('button');
undoChangesButton.className = 'fa fa-history reset-button';
undoChangesButton.className = 'reset-button';
undoChangesButton.title = 'Undo changes';
undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
undoChangesButton.innerHTML +=
document.getElementById('fa-clock-rotate-left').innerHTML;
buttons.insertBefore(undoChangesButton, buttons.firstChild);

View File

@@ -33,7 +33,6 @@
{{/if}}
<!-- Fonts -->
<link rel="stylesheet" href="{{ resource "FontAwesome/css/font-awesome.css" }}">
<link rel="stylesheet" href="{{ resource "fonts/fonts.css" }}">
<!-- Highlight.js Stylesheets -->
@@ -145,10 +144,10 @@
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
{{fa "solid" "bars"}}
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
{{fa "solid" "paintbrush"}}
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
@@ -160,7 +159,7 @@
</ul>
{{#if search_enabled}}
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
<i class="fa fa-search"></i>
{{fa "solid" "magnifying-glass"}}
</button>
{{/if}}
</div>
@@ -170,17 +169,17 @@
<div class="right-buttons">
{{#if print_enable}}
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
{{fa "solid" "print" "print-button"}}
</a>
{{/if}}
{{#if git_repository_url}}
<a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa {{git_repository_icon}}"></i>
{{fa git_repository_icon_class git_repository_icon}}
</a>
{{/if}}
{{#if git_repository_edit_url}}
<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit" rel="edit">
<i id="git-edit-button" class="fa fa-edit"></i>
{{fa "solid" "pencil" "git-edit-button"}}
</a>
{{/if}}
@@ -193,7 +192,7 @@
<div class="search-wrapper">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
<div class="spinner-wrapper">
<i class="fa fa-spinner fa-spin"></i>
{{fa "solid" "spinner" "fa-spin"}}
</div>
</div>
</form>
@@ -224,19 +223,18 @@
{{#if previous}}
<a rel="prev" href="{{ path_to_root }}{{previous.link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
{{#if (eq ../text_direction "rtl")}}
<i class="fa fa-angle-right"></i>
{{fa "solid" "angle-right"}}
{{else}}
<i class="fa fa-angle-left"></i>
{{fa "solid" "angle-left"}}
{{/if}}
</a>
{{/if}}
{{#if next}}
<a rel="next prefetch" href="{{ path_to_root }}{{next.link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
{{#if (eq ../text_direction "rtl")}}
<i class="fa fa-angle-left"></i>
{{fa "solid" "angle-left"}}
{{else}}
<i class="fa fa-angle-right"></i>
{{fa "solid" "angle-right"}}
{{/if}}
</a>
{{/if}}
@@ -250,9 +248,9 @@
{{#if previous}}
<a rel="prev" href="{{ path_to_root }}{{previous.link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
{{#if (eq ../text_direction "rtl")}}
<i class="fa fa-angle-right"></i>
{{fa "solid" "angle-right"}}
{{else}}
<i class="fa fa-angle-left"></i>
{{fa "solid" "angle-left"}}
{{/if}}
</a>
{{/if}}
@@ -260,9 +258,9 @@
{{#if next}}
<a rel="next prefetch" href="{{ path_to_root }}{{next.link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
{{#if (eq text_direction "rtl")}}
<i class="fa fa-angle-left"></i>
{{fa "solid" "angle-left"}}
{{else}}
<i class="fa fa-angle-right"></i>
{{fa "solid" "angle-right"}}
{{/if}}
</a>
{{/if}}
@@ -270,6 +268,12 @@
</div>
<template id=fa-eye>{{fa "solid" "eye"}}</template>
<template id=fa-eye-slash>{{fa "solid" "eye-slash"}}</template>
<template id=fa-copy>{{fa "regular" "copy"}}</template>
<template id=fa-play>{{fa "solid" "play"}}</template>
<template id=fa-clock-rotate-left>{{fa "solid" "clock-rotate-left"}}</template>
{{#if live_reload_endpoint}}
<!-- Livereload script (if served using the cli tool) -->
<script>

View File

@@ -28,7 +28,6 @@
<link rel="stylesheet" href="{{ resource "css/print.css" }}" media="print">
{{/if}}
<!-- Fonts -->
<link rel="stylesheet" href="{{ resource "FontAwesome/css/font-awesome.css" }}">
<link rel="stylesheet" href="{{ resource "fonts/fonts.css" }}">
<!-- Custom theme stylesheets -->
{{#each additional_css}}

View File

@@ -242,6 +242,7 @@ impl HtmlHandlebars {
let rendered = fix_code_blocks(&rendered);
let rendered = add_playground_pre(&rendered, playground_config, edition);
let rendered = hide_lines(&rendered, code_config);
let rendered = convert_fontawesome(&rendered);
rendered
}
@@ -271,6 +272,7 @@ impl HtmlHandlebars {
no_section_label: html_config.no_section_label,
}),
);
handlebars.register_helper("fa", Box::new(helpers::fontawesome::fa_helper));
}
fn emit_redirects(
@@ -635,9 +637,19 @@ fn make_data(
let git_repository_icon = match html_config.git_repository_icon {
Some(ref git_repository_icon) => git_repository_icon,
None => "fa-github",
None => "fab-github",
};
let git_repository_icon_class = match git_repository_icon.split('-').next() {
Some("fa") => "regular",
Some("fas") => "solid",
Some("fab") => "brands",
_ => "regular",
};
data.insert("git_repository_icon".to_owned(), json!(git_repository_icon));
data.insert(
"git_repository_icon_class".to_owned(),
json!(git_repository_icon_class),
);
let mut chapters = vec![];
@@ -737,6 +749,54 @@ fn insert_link_into_header(
)
}
// Convert fontawesome `<i>` tags to inline SVG
fn convert_fontawesome(html: &str) -> String {
use font_awesome_as_a_crate as fa;
let regex = Regex::new(r##"<i([^>]+)class="([^"]+)"([^>]*)></i>"##).unwrap();
regex
.replace_all(html, |caps: &Captures<'_>| {
let text = &caps[0];
let before = &caps[1];
let classes = &caps[2];
let after = &caps[3];
let mut icon = String::new();
let mut type_ = fa::Type::Regular;
let mut other_classes = String::new();
for class in classes.split(" ") {
if let Some(class) = class.strip_prefix("fa-") {
icon = class.to_owned();
} else if class == "fa" {
type_ = fa::Type::Regular;
} else if class == "fas" {
type_ = fa::Type::Solid;
} else if class == "fab" {
type_ = fa::Type::Brands;
} else {
other_classes += " ";
other_classes += class;
}
}
if icon.is_empty() {
text.to_owned()
} else if let Ok(svg) = fa::svg(type_, &icon) {
format!(
r#"<span{before}class="fa-svg{other_classes}"{after}>{svg}</span>"#,
before = before,
other_classes = other_classes,
after = after,
svg = svg
)
} else {
text.to_owned()
}
})
.into_owned()
}
// The rust book uses annotations for rustdoc to test code snippets,
// like the following:
// ```rust,should_panic

View File

@@ -0,0 +1,53 @@
use font_awesome_as_a_crate as fa;
use handlebars::{
Context, Handlebars, Helper, Output, RenderContext, RenderError, RenderErrorReason,
};
use log::trace;
use std::str::FromStr;
pub(crate) fn fa_helper(
h: &Helper<'_>,
_r: &Handlebars<'_>,
_ctx: &Context,
_rc: &mut RenderContext<'_, '_>,
out: &mut dyn Output,
) -> Result<(), RenderError> {
trace!("fa_helper (handlebars helper)");
let type_ = h
.param(0)
.and_then(|v| v.value().as_str())
.and_then(|v| fa::Type::from_str(v).ok())
.ok_or_else(|| {
RenderErrorReason::Other(
"Param 0 with String type is required for fontawesome helper.".to_owned(),
)
})?;
let name = h.param(1).and_then(|v| v.value().as_str()).ok_or_else(|| {
RenderErrorReason::Other(
"Param 1 with String type is required for fontawesome helper.".to_owned(),
)
})?;
trace!("fa_helper: {} {}", type_, name);
let name = name
.strip_prefix("fa-")
.or_else(|| name.strip_prefix("fab-"))
.or_else(|| name.strip_prefix("fas-"))
.unwrap_or(name);
if let Some(id) = h.param(2).and_then(|v| v.value().as_str()) {
out.write(&format!("<span class=fa-svg id=\"{}\">", id))?;
} else {
out.write("<span class=fa-svg>")?;
}
out.write(
fa::svg(type_, name)
.map_err(|_| RenderErrorReason::Other(format!("Missing font {}", name)))?,
)?;
out.write("</span>")?;
Ok(())
}

View File

@@ -1,2 +1,3 @@
pub(crate) mod fontawesome;
pub(crate) mod resources;
pub(crate) mod toc;

View File

@@ -62,28 +62,6 @@ impl StaticFiles {
this.add_builtin("ayu-highlight.css", &theme.ayu_highlight_css);
this.add_builtin("highlight.js", &theme.highlight_js);
this.add_builtin("clipboard.min.js", &theme.clipboard_js);
this.add_builtin("FontAwesome/css/font-awesome.css", theme::FONT_AWESOME);
this.add_builtin(
"FontAwesome/fonts/fontawesome-webfont.eot",
theme::FONT_AWESOME_EOT,
);
this.add_builtin(
"FontAwesome/fonts/fontawesome-webfont.svg",
theme::FONT_AWESOME_SVG,
);
this.add_builtin(
"FontAwesome/fonts/fontawesome-webfont.ttf",
theme::FONT_AWESOME_TTF,
);
this.add_builtin(
"FontAwesome/fonts/fontawesome-webfont.woff",
theme::FONT_AWESOME_WOFF,
);
this.add_builtin(
"FontAwesome/fonts/fontawesome-webfont.woff2",
theme::FONT_AWESOME_WOFF2,
);
this.add_builtin("FontAwesome/fonts/FontAwesome.ttf", theme::FONT_AWESOME_TTF);
if theme.fonts_css.is_none() {
this.add_builtin("fonts/fonts.css", theme::fonts::CSS);
for (file_name, contents) in theme::fonts::LICENSES.iter() {
@@ -170,13 +148,7 @@ impl StaticFiles {
let mut parts = filename.splitn(2, '.');
let parts = parts.next().and_then(|p| Some((p, parts.next()?)));
if let Some((name, suffix)) = parts {
// FontAwesome already does its own cache busting with the ?v=4.7.0 thing,
// and I don't want to have to patch its CSS file to use `{{ resource }}`
if name != ""
&& suffix != ""
&& suffix != "txt"
&& !name.starts_with("FontAwesome/fonts/")
{
if name != "" && suffix != "" && suffix != "txt" {
let hex = hex::encode(&Sha256::digest(data)[..4]);
let new_filename = format!("{}-{}.{}", name, hex, suffix);
self.hash_map.insert(filename.clone(), new_filename.clone());

View File

@@ -29,18 +29,6 @@ pub static TOMORROW_NIGHT_CSS: &[u8] = include_bytes!("../../front-end/css/tomor
pub static HIGHLIGHT_CSS: &[u8] = include_bytes!("../../front-end/css/highlight.css");
pub static AYU_HIGHLIGHT_CSS: &[u8] = include_bytes!("../../front-end/css/ayu-highlight.css");
pub static CLIPBOARD_JS: &[u8] = include_bytes!("../../front-end/js/clipboard.min.js");
pub static FONT_AWESOME: &[u8] = include_bytes!("../../front-end/css/font-awesome.min.css");
pub static FONT_AWESOME_EOT: &[u8] =
include_bytes!("../../front-end/fonts/fontawesome-webfont.eot");
pub static FONT_AWESOME_SVG: &[u8] =
include_bytes!("../../front-end/fonts/fontawesome-webfont.svg");
pub static FONT_AWESOME_TTF: &[u8] =
include_bytes!("../../front-end/fonts/fontawesome-webfont.ttf");
pub static FONT_AWESOME_WOFF: &[u8] =
include_bytes!("../../front-end/fonts/fontawesome-webfont.woff");
pub static FONT_AWESOME_WOFF2: &[u8] =
include_bytes!("../../front-end/fonts/fontawesome-webfont.woff2");
pub static FONT_AWESOME_OTF: &[u8] = include_bytes!("../../front-end/fonts/FontAwesome.otf");
/// The `Theme` struct should be used instead of the static variables because
/// the `new()` method will look if the user has a theme directory in their

View File

@@ -103,7 +103,7 @@ additional-css = ["custom.css", "custom2.css"]
additional-js = ["custom.js"]
no-section-label = false
git-repository-url = "https://github.com/rust-lang/mdBook"
git-repository-icon = "fa-github"
git-repository-icon = "fab-github"
edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}"
site-url = "/example-book/"
cname = "myproject.rs"
@@ -139,7 +139,7 @@ The following configuration options are available:
- **git-repository-url:** A url to the git repository for the book. If provided
an icon link will be output in the menu bar of the book.
- **git-repository-icon:** The FontAwesome icon class to use for the git
repository link. Defaults to `fa-github` which looks like <i class="fa fa-github"></i>.
repository link. Defaults to `fab-github` which looks like <i class="fa fab-github"></i>.
If you are not using GitHub, another option to consider is `fa-code-fork` which looks like <i class="fa fa-code-fork"></i>.
- **edit-url-template:** Edit url template, when provided shows a
"Suggest an edit" button (which looks like <i class="fa fa-edit"></i>) for directly jumping to editing the currently

View File

@@ -363,3 +363,18 @@ fatigue," where people are trained to ignore them because they usually don't
matter for what they're doing.
</div>
## Font-Awesome icons
mdBook includes a copy of [Font Awesome Free's](https://fontawesome.com)
MIT-licensed SVG files. It emulates the `<i>` syntax, but converts the results
to inline SVG. Only the regular, solid, and brands icons are included; paid
features like the light icons are not.
For example, given this HTML syntax:
```hbs
The result looks like this: <i class="fas fa-print"></i>
```
The result looks like this: <i class="fas fa-print"></i>

View File

@@ -86,3 +86,25 @@ and accounts for files that are renamed with a hash in their filename.
```handlebars
<link rel="stylesheet" href="{{ resource "css/chrome.css" }}">
```
### fa
mdBook includes a copy of [Font Awesome Free's](https://fontawesome.com)
MIT-licensed SVG files. It accepts three positional arguments:
1. Type: one of "solid", "regular", and "brands" (light and duotone are not
currently supported)
2. Icon: anything chosen from the
[free icon set](https://fontawesome.com/icons?d=gallery&m=free)
3. ID (optional): if included, an HTML ID attribute will be added to the
icon's wrapping `<span>` tag
For example, this handlebars syntax will become this HTML:
```handlebars
{{fa "solid" "print" "print-button"}}
```
```html
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/></svg></span>
```

View File

@@ -30,12 +30,12 @@ The icons displayed will depend on the settings of how the book was generated.
| Icon | Description |
|------|-------------|
| <i class="fa fa-bars"></i> | Opens and closes the chapter listing sidebar. |
| <i class="fa fa-paint-brush"></i> | Opens a picker to choose a different color theme. |
| <i class="fa fa-search"></i> | Opens a search bar for searching within the book. |
| <i class="fa fa-print"></i> | Instructs the web browser to print the entire book. |
| <i class="fa fa-github"></i> | Opens a link to the website that hosts the source code of the book. |
| <i class="fa fa-edit"></i> | Opens a page to directly edit the source of the page you are currently reading. |
| <i class="fas fa-bars"></i> | Opens and closes the chapter listing sidebar. |
| <i class="fas fa-paintbrush"></i> | Opens a picker to choose a different color theme. |
| <i class="fas fa-magnifying-glass"></i> | Opens a search bar for searching within the book. |
| <i class="fas fa-print"></i> | Instructs the web browser to print the entire book. |
| <i class="fab fa-github"></i> | Opens a link to the website that hosts the source code of the book. |
| <i class="fas fa-pencil"></i> | Opens a page to directly edit the source of the page you are currently reading. |
Tapping the menu bar will scroll the page to the top.
@@ -59,9 +59,9 @@ Code blocks may contain several different icons for interacting with them:
| Icon | Description |
|------|-------------|
| <i class="fa fa-copy"></i> | Copies the code block into your local clipboard, to allow pasting into another application. |
| <i class="fa fa-play"></i> | For Rust code examples, this will execute the sample code and display the compiler output just below the example (see [playground]). |
| <i class="fas fa-play"></i> | For Rust code examples, this will execute the sample code and display the compiler output just below the example (see [playground]). |
| <i class="fa fa-eye"></i> | For Rust code examples, this will toggle visibility of "hidden" lines. Sometimes, larger examples will hide lines which are not particularly relevant to what is being illustrated (see [hiding code lines]). |
| <i class="fa fa-history"></i> | For [editable code examples][editor], this will undo any changes you have made. |
| <i class="fas fa-clock-rotate-left"></i> | For [editable code examples][editor], this will undo any changes you have made. |
Here's an example: