Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
book
|
||||
6
book.toml
Normal file
6
book.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[book]
|
||||
title = "Solstice Docs"
|
||||
authors = ["Maxwell Jeffress"]
|
||||
language = "en"
|
||||
|
||||
[output.html]
|
||||
14
src/SUMMARY.md
Normal file
14
src/SUMMARY.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Summary
|
||||
|
||||
[Introduction](introduction.md)
|
||||
|
||||
# Specification
|
||||
- [Reserved Words](reserved_words.md)
|
||||
- [Comments](comments.md)
|
||||
- [Expressions](expressions.md)
|
||||
- [Types](types.md)
|
||||
- [Variables](variables.md)
|
||||
- [Scoping](scoping.md)
|
||||
- [Operators](operators.md)
|
||||
- [Function Definition](function_definition.md)
|
||||
- [Struct Definition](struct_definition.md)
|
||||
41
src/comments.md
Normal file
41
src/comments.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Comments
|
||||
|
||||
Comments in Solstice can be done in 3 ways:
|
||||
|
||||
## `//` (Single-Line Comment)
|
||||
|
||||
Use `//` to tell Solstice to ignore text until the end of the line.
|
||||
|
||||
This kind of comment should be used inside code to explain how parts of code work.
|
||||
|
||||
## `/* */` (Multiline Comment)
|
||||
|
||||
Use `/*` and `*/` to tell Solstice to ignore text in between the two markers.
|
||||
|
||||
This kind of comment should be used for explainations of how functions and structs work, placed above the definition.
|
||||
|
||||
## `#` (Shebang Comment)
|
||||
|
||||
Use `#` to tell Solstice to ignore text until the end of the line.
|
||||
|
||||
This kind of comment should be used in a shebang (writing `#!/usr/bin/env solstice` at the top of your entry point source file), not in the middle.
|
||||
|
||||
## Example
|
||||
|
||||
```solstice
|
||||
#!/usr/bin/env solstice
|
||||
|
||||
/*
|
||||
This function does something.
|
||||
Input:
|
||||
funnyNumber: A funny number
|
||||
Returns: Another funny number
|
||||
*/
|
||||
def doSomething(int funnyNumber) int {
|
||||
// Tell the user the number is funny
|
||||
puts "The number" funnyNumber "is very funny"
|
||||
|
||||
// Now give them another funny number
|
||||
return 532
|
||||
}
|
||||
```
|
||||
147
src/expressions.md
Normal file
147
src/expressions.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Expressions
|
||||
|
||||
An expression in Solstice is a combination of operators and values (whether literal or stored in a variable).;
|
||||
|
||||
## Expression Types
|
||||
|
||||
"Precedence" is a number which dictates the order of execution. The higher the precedence, the sooner an expression will be evaluated.
|
||||
|
||||
### Binary Mathematical
|
||||
|
||||
* `+`: Adds two numbers on either side, or concatenates two strings
|
||||
* Accepts:
|
||||
* Two integers either side
|
||||
* Two doubles either side
|
||||
* Two strings either side
|
||||
* An integer and a double on either side (order not significant)
|
||||
* Returns:
|
||||
* The sum of both values as an integer if both values are integers
|
||||
* The sum of both values as a double if one value is a double and the other is an int, or both values are doubles
|
||||
* The concatenated string when both values are strings
|
||||
* Precedence: 5
|
||||
* `-`: Subtracts two numbers on either side
|
||||
* Accepts:
|
||||
* Two integers either side
|
||||
* Two doubles either side
|
||||
* An integer and a double on either side (order not significant)
|
||||
* Returns:
|
||||
* The difference of both values as an integer if both values are integers
|
||||
* The difference of both values as a double if one value is a double and the other is an int, or both values are doubles
|
||||
* Precedence: 5
|
||||
* `*`: Multiplies two numbers on either side
|
||||
* Accepts:
|
||||
* Two integers either side
|
||||
* Two doubles either side
|
||||
* An integer and a double on either side (order not significant)
|
||||
* Returns:
|
||||
* The product of both values as an integer if both values are integers
|
||||
* The product of both values as a double if one value is a double and the other is an int, or both values are doubles
|
||||
* Precedence: 6
|
||||
* `/`: Divides two numbers on either side
|
||||
* Accepts:
|
||||
* Two integers either side
|
||||
* Two doubles either side
|
||||
* An integer and a double on either side (order not significant)
|
||||
* Returns:
|
||||
* The quotient of both values as an integer if both values are integers
|
||||
* The quotient of both values as a double if one value is a double and the other is an int, or both values are doubles
|
||||
* Precedence: 6
|
||||
|
||||
### Binary Comparative
|
||||
|
||||
* `==`: Checks two values to determine if they are equal
|
||||
* Accepts:
|
||||
* Any two values either side which are of the same type
|
||||
* An integer and a double on either side (order not significant)
|
||||
* Returns:
|
||||
* `true` if both provided values are exactly the same. 
|
||||
* `false` otherwise.
|
||||
* Precedence: 2
|
||||
* `!=`: Checks two values to determine if they are not equal
|
||||
* Accepts:
|
||||
* Any two values either side which are of the same type
|
||||
* An integer and a double on either side (order not significant)
|
||||
* Returns:
|
||||
* `true` if both provided values are not exactly the same. 
|
||||
* `false` otherwise.
|
||||
* Precedence: 2
|
||||
* `>`: Checks two numbers to determine which is greater
|
||||
* Accepts:
|
||||
* Any two numbers (int or double) either side
|
||||
* Returns:
|
||||
* `true` if the number provided on the left is greater than the number provided on the right.
|
||||
* `false` otherwise.
|
||||
* Precedence: 2
|
||||
* `<`: Checks two numbers to determine which is lesser
|
||||
* Accepts:
|
||||
* Any two numbers (int or double) either side
|
||||
* Returns:
|
||||
* `true` if the number provided on the left is lesser than the number provided on the right.
|
||||
* `false` otherwise.
|
||||
* Precedence: 2
|
||||
* `>=`: Checks two numbers to determine which is greater, or whether both are equal
|
||||
* Accepts:
|
||||
* Any two numbers (int or double) either side
|
||||
* Returns:
|
||||
* `true` if the number provided on the left is greater than the number provided on the right, or if both numbers are equal.
|
||||
* `false` otherwise.
|
||||
* Precedence: 2
|
||||
* `<=`: Checks two numbers to determine which is lesser, or whether both are equal
|
||||
* Accepts:
|
||||
* Any two numbers (int or double) either side
|
||||
* Returns:
|
||||
* `true` if the number provided on the left is lesser than the number provided on the right, or if both numbers are equal.
|
||||
* `false` otherwise.
|
||||
* Precedence: 2
|
||||
|
||||
### Binary Misc
|
||||
* `as`: Converts between types.
|
||||
* Accepts:
|
||||
* Any object on the left, and a type which has an applicable conversion defined by the object on the right
|
||||
* Returns:
|
||||
* The object turned into the type on the right, in the way defined by the as-method
|
||||
* `.`: Accesses object fields
|
||||
* Accepts:
|
||||
* An object on the left, and an identifier on the right which corresponds to a field or method in the object
|
||||
* Returns:
|
||||
* The object field or method the identifier corresponds to
|
||||
|
||||
### Brackets
|
||||
|
||||
* `(...)`: Evaluates an expression inside the brackets before outside expressions.
|
||||
* Accepts:
|
||||
* Any expression inside the brackets (in place of `...`)
|
||||
* Returns:
|
||||
* The result of the expression in the brackets
|
||||
* Precedence: 7
|
||||
|
||||
### Function Call
|
||||
|
||||
* `(...)`: Calls the specified function.
|
||||
* Accepts:
|
||||
* An identifier for the function before the brackets, then comma-seperated expressions corresponding to the function type signature (see [Types -> Combined Types -> fun](types.md#fun)) inside the brackets (in place of `...`)
|
||||
* Returns:
|
||||
* The function's return type as specified by the function type signature (see [Types -> Combined Types -> fun](types.md#fun))
|
||||
|
||||
### Unary Misc
|
||||
|
||||
* `new`: Creates a new object from a template.
|
||||
* Accepts:
|
||||
* A template after the `new` keyword.
|
||||
* A core type identifier
|
||||
* Returns:
|
||||
* An object based on the provided template, if provided with a template.
|
||||
* The empty version of the core type, if provided with a core type, which is:
|
||||
* `int`: `0`
|
||||
* `double`: `0.0`
|
||||
* `string`: `""`
|
||||
* `char`: `'\0'`
|
||||
* `bool`: `false`
|
||||
* `sizeof`: Gets the size of something.
|
||||
* Accepts:
|
||||
* A string
|
||||
* An object with an `int size` field
|
||||
* Returns:
|
||||
* The string length if provided a string
|
||||
* The `int size` field of the object if provided an object
|
||||
|
||||
61
src/function_definition.md
Normal file
61
src/function_definition.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Function Definition
|
||||
|
||||
## Named Functions
|
||||
|
||||
Define a named function with the following syntax:
|
||||
|
||||
```solstice
|
||||
def functionName(args) type {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
where:
|
||||
|
||||
* `functionName` is the name of the function.
|
||||
* If `functionName` is already defined as a function or other value, it will only be overwritten if the type does not change.
|
||||
* `args` is multiple comma-seperated pairs of:
|
||||
* a type identifier for the parameter, and
|
||||
* an identifier (representing the parameter name)
|
||||
* `type` is the type of value which the function will return
|
||||
* `...` is the code inside the function (the function body).
|
||||
* The function body will have access to previously set variables through a closure. The closure is a copy of all variables in their current state at function definition time.
|
||||
* The function body will also have access to the parameters defined in `args`.
|
||||
|
||||
Example:
|
||||
|
||||
```solstice
|
||||
def add(int a, int b) int {
|
||||
return a + b
|
||||
}
|
||||
```
|
||||
|
||||
## Anonymous/Lambda functions
|
||||
|
||||
Define a lambda function with the following syntax:
|
||||
|
||||
```solstice
|
||||
lambda(args) type {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
where:
|
||||
|
||||
* `args` is multiple comma-seperated pairs of:
|
||||
* a type identifier for the parameter, and
|
||||
* an identifier (representing the parameter name)
|
||||
* `type` is the type of value which the function will return
|
||||
* `...` is the code inside the function (the function body).
|
||||
* The function body will have access to previously set variables through a closure. The closure is a copy of all variables in their current state at function definition time.
|
||||
* The function body will also have access to the parameters defined in `args`.
|
||||
|
||||
Example:
|
||||
|
||||
```solstice
|
||||
add = lambda(int a, int b) int {
|
||||
return a + b
|
||||
}
|
||||
```
|
||||
|
||||
Lambda functions are most useful when passed as arguments to other functions.
|
||||
5
src/introduction.md
Normal file
5
src/introduction.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Introduction
|
||||
|
||||
Welcome to the Solstice docs! For now, the documentation is in the format of a specification, which instructs you on how Solstice should behave. In future, a tutorial (similar to the Rust book) will be written which shows you each part of the language, instead of declaring it like the spec.
|
||||
|
||||
Docs generated by mdBook.
|
||||
123
src/operators.md
Normal file
123
src/operators.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Operators
|
||||
|
||||
Operators are bits of code intended to control how the program runs.
|
||||
|
||||
## `puts`
|
||||
|
||||
Stands for "put something". Prints debug info to the console, followed by a new line.
|
||||
|
||||
### Formatting
|
||||
|
||||
* int: The literal number, as formatted by C `printf("%" PRId64)`.
|
||||
* double: The literal number, as formatted by C `printf("%f")`.
|
||||
* string: All characters in the string, as formatted by C `printf("%s")`.
|
||||
* char: The ASCII character corresponding to the number held, as formatted by C `printf("%c")`.
|
||||
* bool: If `true` or `1`, print `true`. Otherwise, print `false`.
|
||||
|
||||
* fun: Print `<function>`.
|
||||
* template: Print `<struct fields: { ... }>`, where:
|
||||
* `...` is a comma-seperated sequence of `key: value`, where:
|
||||
* `key` is the identifier of the field.
|
||||
* `value` is the properly formatted value in the field.
|
||||
* object: Print `<object fields: { ... }>`, where:
|
||||
* `...` is a comma-seperated sequence of `key: value`, where:
|
||||
* `key` is the identifier of the field.
|
||||
* `value` is the properly formatted value in the field.
|
||||
|
||||
## `if`
|
||||
|
||||
Runs a block of code only if the provided condition is true.
|
||||
|
||||
Syntax:
|
||||
|
||||
```solstice
|
||||
if condition {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
where:
|
||||
* `condition` is an expression which evaluates to a boolean.
|
||||
* If this expression evaluates to `true`, the code block will be run.
|
||||
* Brackets surrounding the condition are optional.
|
||||
* `...` is the code to run if the condition is true.
|
||||
|
||||
Example:
|
||||
|
||||
```solstice
|
||||
if 2 + 2 == 4 {
|
||||
puts "phew!"
|
||||
}
|
||||
```
|
||||
|
||||
## `while`
|
||||
|
||||
Runs a block of code until the provided condition is false.
|
||||
|
||||
Syntax:
|
||||
|
||||
```solstice
|
||||
while condition {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
where:
|
||||
* `condition` is an expression which evaluates to a boolean.
|
||||
* If this expression evaluates to `false`, the code block will not be run anymore.
|
||||
* Brackets surrounding the condition are optional.
|
||||
* `...` is the code to run while the condition is true.
|
||||
|
||||
Example:
|
||||
|
||||
```solstice
|
||||
while true {
|
||||
puts "To infinity and beyond!"
|
||||
}
|
||||
```
|
||||
|
||||
## `return`
|
||||
|
||||
Returns a value from a function.
|
||||
|
||||
Syntax:
|
||||
|
||||
```solstice
|
||||
return value
|
||||
```
|
||||
|
||||
where `value` is a value with the same type as the function's return type.
|
||||
|
||||
## `ground`
|
||||
|
||||
**Inline Ground is potentially dangerous. Only use if you know what you're doing!**
|
||||
|
||||
Inserts inline Ground code into the generated program.
|
||||
|
||||
Syntax:
|
||||
|
||||
```solstice
|
||||
ground {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
where `...` is the Ground code to insert.
|
||||
|
||||
See [here](https://chookspace.com/ground/ground) for details about Ground.
|
||||
|
||||
Note: **Solstice cannot keep track of state inside `ground` blocks!** Ground will allow you to do things without the overhead of the Solstice type checker.
|
||||
|
||||
To extract a value from a Ground block, do something like this:
|
||||
|
||||
```solstice
|
||||
x = 0
|
||||
ground {
|
||||
set &x 10
|
||||
}
|
||||
|
||||
puts x
|
||||
```
|
||||
|
||||
so the type checker can know what `x` is.
|
||||
|
||||
27
src/reserved_words.md
Normal file
27
src/reserved_words.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Reserved Words
|
||||
|
||||
This is a list of all reserved words in Solstice, seperated by new lines. These words may not be used as identifiers.
|
||||
|
||||
After `//`, notes may be provided which detail exceptions to the reserved word, however, the word should still be treated as reserved regardless.
|
||||
|
||||
```solstice
|
||||
puts
|
||||
if
|
||||
while
|
||||
def
|
||||
lambda
|
||||
return
|
||||
use
|
||||
struct
|
||||
enum
|
||||
constructor
|
||||
destructor
|
||||
duplicator
|
||||
private
|
||||
protected
|
||||
ground // Only when targeting GroundVM
|
||||
new
|
||||
as
|
||||
sizeof
|
||||
pragma
|
||||
```
|
||||
43
src/scoping.md
Normal file
43
src/scoping.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Scoping and Lifetimes
|
||||
|
||||
## Subscoping
|
||||
|
||||
A "subscope" is a scope contained inside a function, created inside curly braces (`{}`).
|
||||
|
||||
Subscopes can view and modify variables avaliable in their parent scope.
|
||||
|
||||
Subscopes may also create their own variables, however these will be destroyed when the subscope ends.
|
||||
|
||||
## Function Scoping
|
||||
|
||||
A function scope is a scope created inside a function.
|
||||
|
||||
Function scopes are created from a function's attached closure, which is a snapshot of all variables at the time of function definition.
|
||||
|
||||
The function's attached closure cannot be modified.
|
||||
|
||||
Function scopes also contain all arguments provided to a function.
|
||||
|
||||
When the function finishes running (with a `return`), all variables are destroyed.
|
||||
|
||||
## Destructors
|
||||
|
||||
Objects (see [here](struct_definition.md)) can contain a destructor which specifies custom behaviour for destroying itself. This is useful for:
|
||||
|
||||
* Freeing heap-allocated memory
|
||||
* Closing a connection
|
||||
* Closing a file
|
||||
|
||||
The destructor will always be in the field `destructor` with type signature `fun() int`. It should be automatically called when the variable goes out of scope.
|
||||
|
||||
## Duplicators
|
||||
|
||||
Objects (see [here](struct_definition.md)) can contain a duplicator which specifies custom behaviour for copying itself. This is useful for:
|
||||
|
||||
* Duplicating heap-allocated memory
|
||||
|
||||
The duplicator will always be in the field `duplicator` with type signature `fun((self) old) (self)`. It should be automatically called when the variable is:
|
||||
|
||||
* Copied into a new variable
|
||||
* Put inside a closure
|
||||
* Passed to a function
|
||||
191
src/struct_definition.md
Normal file
191
src/struct_definition.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# Struct Definition
|
||||
|
||||
Define a struct with the `struct` keyword.
|
||||
|
||||
Syntax:
|
||||
|
||||
```
|
||||
struct StructName {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
where:
|
||||
|
||||
* `StructName` is the name of your struct
|
||||
* `...` is the body of your struct
|
||||
|
||||
## Struct Body
|
||||
|
||||
Inside the struct body, you can:
|
||||
|
||||
* Define fields
|
||||
* Define methods
|
||||
* Define special methods (`constructor`, `destructor`, `duplicator`, `as`)
|
||||
|
||||
## Defining fields
|
||||
|
||||
A field in a struct is defined in the same way as a variable.
|
||||
|
||||
```
|
||||
struct MyStruct {
|
||||
x = 5
|
||||
}
|
||||
```
|
||||
|
||||
This creates a field named `x` with a default value of `5`.
|
||||
|
||||
## Defining methods
|
||||
|
||||
A method in a struct is defined in the same way as a named function.
|
||||
|
||||
```
|
||||
struct MyStruct {
|
||||
def myMethod() int {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Methods have access to the struct's members through the `self` object, which is implicitly passed.
|
||||
|
||||
|
||||
```
|
||||
struct MyStruct {
|
||||
|
||||
x = 10
|
||||
|
||||
def myMethod() int {
|
||||
return self.x * 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Private and protected fields
|
||||
|
||||
A private field is a field which only methods inside the object can read and write.
|
||||
|
||||
A protected field is a field which only methods inside the object can write to, however anywhere else in the program the field can be read.
|
||||
|
||||
All other fields are public, meaning anywhere in the program can read or write these fields.
|
||||
|
||||
Methods and special methods are by default protected, however can be made private.
|
||||
|
||||
```
|
||||
struct MyStruct {
|
||||
private x = 10
|
||||
protected y = "Hi"
|
||||
|
||||
private doSomething() int {
|
||||
return 14
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Defining special methods
|
||||
|
||||
In Solstice, objects have some special methods which provide ease of use while handling these objects.
|
||||
|
||||
### Constructor
|
||||
|
||||
A constructor is used to initialize an object with user-provided fields.
|
||||
|
||||
Define a constructor like this:
|
||||
|
||||
```
|
||||
struct MyStruct {
|
||||
constructor(args) {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
where:
|
||||
* `args` is multiple comma-seperated pairs of:
|
||||
* a type identifier for the parameter, and
|
||||
* an identifier (representing the parameter name)
|
||||
* `...` is the constructor body
|
||||
* The constructor body has access to the `self` object, which is how it can modify the newly created object.
|
||||
* The `self` object is automatically returned at the end of the constructor, however can be returned manually if required.
|
||||
|
||||
Use a constructor like this:
|
||||
|
||||
```
|
||||
MyStruct(args)
|
||||
```
|
||||
|
||||
where:
|
||||
* args is many comma-seperated expressions corresponding to the signature of the defined constructor
|
||||
|
||||
### Destructor
|
||||
|
||||
A destructor is used to destroy an object once it goes out of scope.
|
||||
|
||||
Define a destructor like this:
|
||||
|
||||
```
|
||||
struct MyStruct {
|
||||
destructor {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
where:
|
||||
* `...` is the destructor body
|
||||
* The destructor body has access to the `self` object, which is how it can access and modify the object which is being destroyed.
|
||||
* There is no need to return a value from the destructor.
|
||||
|
||||
Note how the destructor does not take any arguments.
|
||||
|
||||
### Duplicator
|
||||
|
||||
A duplicator is used to copy an object when it is assigned to a new name, or passed to a function.
|
||||
|
||||
Define a duplicator like this:
|
||||
|
||||
```
|
||||
struct MyStruct {
|
||||
duplicator {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
where:
|
||||
* `...` is the duplicator body
|
||||
* The destructor body has access to the `self` and `old` objects.
|
||||
* The `self` object is the new duplication and will be automatically returned
|
||||
* The `old` object is the old object and will not be copied out
|
||||
|
||||
Note how the duplicator does not take any arguments.
|
||||
|
||||
### As methods
|
||||
|
||||
As methods allow easy conversion between different types.
|
||||
|
||||
Define an as method like this:
|
||||
|
||||
```
|
||||
struct MyStruct {
|
||||
as type {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
where:
|
||||
* `type` is the type which the object will be converted to
|
||||
* `...` is the as method body
|
||||
* The `self` object is avaliable inside the as method
|
||||
* You will need to initialize and return a new value with the specified type
|
||||
|
||||
Use an as method like this:
|
||||
|
||||
```
|
||||
myobj as type
|
||||
```
|
||||
|
||||
where:
|
||||
* `myobj` is an object with an as method which provides a conversion to `type`
|
||||
* `type` is the target type
|
||||
108
src/types.md
Normal file
108
src/types.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Types
|
||||
|
||||
Solstice is statically typed, meaning all values must have a known type before any code can be executed.
|
||||
|
||||
## Core Types
|
||||
|
||||
These core types are automatically avaliable in Solstice. The words to identify them are reserved and cannot be reassigned.
|
||||
|
||||
### `int`
|
||||
|
||||
8-byte signed integer, equivalent to C `int64_t` or Ground `-int`.
|
||||
|
||||
Examples: `32`, `121`, `-5`
|
||||
|
||||
### `double`
|
||||
|
||||
8-byte double prescision floating point number, equivalent to C `double` or Ground `-double`.
|
||||
|
||||
Examples: `3.14`, `-2.7`
|
||||
|
||||
### `string`
|
||||
|
||||
C-style null-terminated array of characters. Equivalent to C `char*` or Ground `-string`.
|
||||
|
||||
### `char`
|
||||
|
||||
1-byte signed integer, usually used to store a character, however can also be used to store bytes. Equivalent to C `char` or Ground `-char`.
|
||||
|
||||
### `bool`
|
||||
|
||||
1-byte unsigned integer, either 1 or 0. Represented by the reserved words `true` and `false`. Equivalent to C `char` (no stdbool.h) or `bool` (with stdbool.h) or Ground `-bool`.
|
||||
|
||||
## Combined Types
|
||||
|
||||
These types utilise core types to create new combinations. They take type arguments.
|
||||
|
||||
### `fun`
|
||||
|
||||
Represents a function. Equivalent to Ground `-function`, or a C function pointer.
|
||||
|
||||
**Syntax**:
|
||||
|
||||
```solstice
|
||||
fun(...) type
|
||||
```
|
||||
|
||||
where:
|
||||
|
||||
* `type` is a type identifier, which will be the return type
|
||||
* `...` is multiple type identifiers, seperated by commas, which are the argument types
|
||||
|
||||
**Example**:
|
||||
|
||||
```solstice
|
||||
fun(int, string) bool
|
||||
```
|
||||
|
||||
This represents a function which takes an `int` and `string` as arguments, and returns a `bool`.
|
||||
|
||||
### `template`
|
||||
|
||||
Represents an uninitialized object. Equivalent to Ground `-struct`.
|
||||
|
||||
**Syntax**:
|
||||
|
||||
```solstice
|
||||
template(...)
|
||||
```
|
||||
|
||||
where `...` is multiple pairs of:
|
||||
|
||||
* a type identifier for the field, and
|
||||
* an identifier (representing the field name)
|
||||
|
||||
seperated by commas.
|
||||
|
||||
**Example**:
|
||||
|
||||
```solstice
|
||||
template(int x, string y)
|
||||
```
|
||||
|
||||
This represents a template which, when initialized, has fields x (with integer) and y (with string).
|
||||
|
||||
### `object`
|
||||
|
||||
Represents an initialized object.
|
||||
|
||||
**Syntax**:
|
||||
|
||||
```solstice
|
||||
object(...)
|
||||
```
|
||||
|
||||
where `...` is multiple pairs of:
|
||||
|
||||
* a type identifier for the field, and
|
||||
* an identifier (representing the field name)
|
||||
|
||||
seperated by commas.
|
||||
|
||||
**Example**:
|
||||
|
||||
```solstice
|
||||
object(int x, string y)
|
||||
```
|
||||
|
||||
This represents a object which has fields x (with integer) and y (with string).
|
||||
14
src/variables.md
Normal file
14
src/variables.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Variables
|
||||
|
||||
Variables are defined and modified with the syntax:
|
||||
|
||||
```solstice
|
||||
name = value
|
||||
```
|
||||
|
||||
where:
|
||||
|
||||
* `name` is an identifier, and
|
||||
* `value` is a literal, expression, or identifier previously assigned to a variable.
|
||||
|
||||
All variables must have a known type and value at definition time. When updating a variable, the type may not change.
|
||||
843
theme/book.js
Normal file
843
theme/book.js
Normal file
@@ -0,0 +1,843 @@
|
||||
'use strict';
|
||||
|
||||
/* global default_theme, default_dark_theme, default_light_theme, hljs, ClipboardJS */
|
||||
|
||||
// Fix back button cache problem
|
||||
window.onunload = function() { };
|
||||
|
||||
// Global variable, shared between modules
|
||||
function playground_text(playground, hidden = true) {
|
||||
const code_block = playground.querySelector('code');
|
||||
|
||||
if (window.ace && code_block.classList.contains('editable')) {
|
||||
const editor = window.ace.edit(code_block);
|
||||
return editor.getValue();
|
||||
} else if (hidden) {
|
||||
return code_block.textContent;
|
||||
} else {
|
||||
return code_block.innerText;
|
||||
}
|
||||
}
|
||||
|
||||
(function codeSnippets() {
|
||||
function fetch_with_timeout(url, options, timeout = 6000) {
|
||||
return Promise.race([
|
||||
fetch(url, options),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)),
|
||||
]);
|
||||
}
|
||||
|
||||
const playgrounds = Array.from(document.querySelectorAll('.playground'));
|
||||
if (playgrounds.length > 0) {
|
||||
fetch_with_timeout('https://play.rust-lang.org/meta/crates', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
// get list of crates available in the rust playground
|
||||
const playground_crates = response.crates.map(item => item['id']);
|
||||
playgrounds.forEach(block => handle_crate_list_update(block, playground_crates));
|
||||
});
|
||||
}
|
||||
|
||||
function handle_crate_list_update(playground_block, playground_crates) {
|
||||
// update the play buttons after receiving the response
|
||||
update_play_button(playground_block, playground_crates);
|
||||
|
||||
// and install on change listener to dynamically update ACE editors
|
||||
if (window.ace) {
|
||||
const code_block = playground_block.querySelector('code');
|
||||
if (code_block.classList.contains('editable')) {
|
||||
const editor = window.ace.edit(code_block);
|
||||
editor.addEventListener('change', () => {
|
||||
update_play_button(playground_block, playground_crates);
|
||||
});
|
||||
// add Ctrl-Enter command to execute rust code
|
||||
editor.commands.addCommand({
|
||||
name: 'run',
|
||||
bindKey: {
|
||||
win: 'Ctrl-Enter',
|
||||
mac: 'Ctrl-Enter',
|
||||
},
|
||||
exec: _editor => run_rust_code(playground_block),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// updates the visibility of play button based on `no_run` class and
|
||||
// used crates vs ones available on https://play.rust-lang.org
|
||||
function update_play_button(pre_block, playground_crates) {
|
||||
const play_button = pre_block.querySelector('.play-button');
|
||||
|
||||
// skip if code is `no_run`
|
||||
if (pre_block.querySelector('code').classList.contains('no_run')) {
|
||||
play_button.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
// get list of `extern crate`'s from snippet
|
||||
const txt = playground_text(pre_block);
|
||||
const re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g;
|
||||
const snippet_crates = [];
|
||||
let item;
|
||||
while (item = re.exec(txt)) {
|
||||
snippet_crates.push(item[1]);
|
||||
}
|
||||
|
||||
// check if all used crates are available on play.rust-lang.org
|
||||
const all_available = snippet_crates.every(function(elem) {
|
||||
return playground_crates.indexOf(elem) > -1;
|
||||
});
|
||||
|
||||
if (all_available) {
|
||||
play_button.classList.remove('hidden');
|
||||
play_button.hidden = false;
|
||||
} else {
|
||||
play_button.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function run_rust_code(code_block) {
|
||||
let result_block = code_block.querySelector('.result');
|
||||
if (!result_block) {
|
||||
result_block = document.createElement('code');
|
||||
result_block.className = 'result hljs language-bash';
|
||||
|
||||
code_block.append(result_block);
|
||||
}
|
||||
|
||||
const text = playground_text(code_block);
|
||||
const classes = code_block.querySelector('code').classList;
|
||||
let edition = '2015';
|
||||
classes.forEach(className => {
|
||||
if (className.startsWith('edition')) {
|
||||
edition = className.slice(7);
|
||||
}
|
||||
});
|
||||
const params = {
|
||||
version: 'stable',
|
||||
optimize: '0',
|
||||
code: text,
|
||||
edition: edition,
|
||||
};
|
||||
|
||||
if (text.indexOf('#![feature') !== -1) {
|
||||
params.version = 'nightly';
|
||||
}
|
||||
|
||||
result_block.innerText = 'Running...';
|
||||
|
||||
fetch_with_timeout('https://play.rust-lang.org/evaluate.json', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
body: JSON.stringify(params),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (response.result.trim() === '') {
|
||||
result_block.innerText = 'No output';
|
||||
result_block.classList.add('result-no-output');
|
||||
} else {
|
||||
result_block.innerText = response.result;
|
||||
result_block.classList.remove('result-no-output');
|
||||
}
|
||||
})
|
||||
.catch(error => result_block.innerText = 'Playground Communication: ' + error.message);
|
||||
}
|
||||
|
||||
// Syntax highlighting Configuration
|
||||
hljs.configure({
|
||||
tabReplace: ' ', // 4 spaces
|
||||
languages: [], // Languages used for auto-detection
|
||||
});
|
||||
|
||||
const code_nodes = Array
|
||||
.from(document.querySelectorAll('code'))
|
||||
// Don't highlight `inline code` blocks in headers.
|
||||
.filter(function(node) {
|
||||
return !node.parentElement.classList.contains('header');
|
||||
});
|
||||
|
||||
if (window.ace) {
|
||||
// language-rust class needs to be removed for editable
|
||||
// blocks or highlightjs will capture events
|
||||
code_nodes
|
||||
.filter(function(node) {
|
||||
return node.classList.contains('editable');
|
||||
})
|
||||
.forEach(function(block) {
|
||||
block.classList.remove('language-rust');
|
||||
});
|
||||
|
||||
code_nodes
|
||||
.filter(function(node) {
|
||||
return !node.classList.contains('editable');
|
||||
})
|
||||
.forEach(function(block) {
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
} else {
|
||||
code_nodes.forEach(function(block) {
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
}
|
||||
|
||||
// Adding the hljs class gives code blocks the color css
|
||||
// even if highlighting doesn't apply
|
||||
code_nodes.forEach(function(block) {
|
||||
block.classList.add('hljs');
|
||||
});
|
||||
|
||||
Array.from(document.querySelectorAll('code.hljs')).forEach(function(block) {
|
||||
|
||||
const lines = Array.from(block.querySelectorAll('.boring'));
|
||||
// If no lines were hidden, return
|
||||
if (!lines.length) {
|
||||
return;
|
||||
}
|
||||
block.classList.add('hide-boring');
|
||||
|
||||
const buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
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);
|
||||
|
||||
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 (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');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (window.playground_copyable) {
|
||||
Array.from(document.querySelectorAll('pre code')).forEach(function(block) {
|
||||
const pre_block = block.parentNode;
|
||||
if (!pre_block.classList.contains('playground')) {
|
||||
let buttons = pre_block.querySelector('.buttons');
|
||||
if (!buttons) {
|
||||
buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
}
|
||||
|
||||
const clipButton = document.createElement('button');
|
||||
clipButton.className = 'clip-button';
|
||||
clipButton.title = 'Copy to clipboard';
|
||||
clipButton.setAttribute('aria-label', clipButton.title);
|
||||
clipButton.innerHTML = '<i class="tooltiptext"></i>';
|
||||
|
||||
buttons.insertBefore(clipButton, buttons.firstChild);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Process playground code blocks
|
||||
Array.from(document.querySelectorAll('.playground')).forEach(function(pre_block) {
|
||||
// Add play button
|
||||
let buttons = pre_block.querySelector('.buttons');
|
||||
if (!buttons) {
|
||||
buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
}
|
||||
|
||||
const runCodeButton = document.createElement('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', () => {
|
||||
run_rust_code(pre_block);
|
||||
});
|
||||
|
||||
if (window.playground_copyable) {
|
||||
const copyCodeClipboardButton = document.createElement('button');
|
||||
copyCodeClipboardButton.className = 'clip-button';
|
||||
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
|
||||
copyCodeClipboardButton.title = 'Copy to clipboard';
|
||||
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
|
||||
|
||||
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
|
||||
}
|
||||
|
||||
const code_block = pre_block.querySelector('code');
|
||||
if (window.ace && code_block.classList.contains('editable')) {
|
||||
const undoChangesButton = document.createElement('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);
|
||||
|
||||
undoChangesButton.addEventListener('click', function() {
|
||||
const editor = window.ace.edit(code_block);
|
||||
editor.setValue(editor.originalCode);
|
||||
editor.clearSelection();
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function themes() {
|
||||
const html = document.querySelector('html');
|
||||
const themeToggleButton = document.getElementById('mdbook-theme-toggle');
|
||||
const themePopup = document.getElementById('mdbook-theme-list');
|
||||
const themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
|
||||
const themeIds = [];
|
||||
themePopup.querySelectorAll('button.theme').forEach(function(el) {
|
||||
themeIds.push(el.id);
|
||||
});
|
||||
const stylesheets = {
|
||||
ayuHighlight: document.querySelector('#mdbook-ayu-highlight-css'),
|
||||
tomorrowNight: document.querySelector('#mdbook-tomorrow-night-css'),
|
||||
highlight: document.querySelector('#mdbook-highlight-css'),
|
||||
};
|
||||
|
||||
function showThemes() {
|
||||
themePopup.style.display = 'block';
|
||||
themeToggleButton.setAttribute('aria-expanded', true);
|
||||
themePopup.querySelector('button#mdbook-theme-' + get_theme()).focus();
|
||||
}
|
||||
|
||||
function updateThemeSelected() {
|
||||
themePopup.querySelectorAll('.theme-selected').forEach(function(el) {
|
||||
el.classList.remove('theme-selected');
|
||||
});
|
||||
const selected = get_saved_theme() ?? 'default_theme';
|
||||
let element = themePopup.querySelector('button#mdbook-theme-' + selected);
|
||||
if (element === null) {
|
||||
// Fall back in case there is no "Default" item.
|
||||
element = themePopup.querySelector('button#mdbook-theme-' + get_theme());
|
||||
}
|
||||
element.classList.add('theme-selected');
|
||||
}
|
||||
|
||||
function hideThemes() {
|
||||
themePopup.style.display = 'none';
|
||||
themeToggleButton.setAttribute('aria-expanded', false);
|
||||
themeToggleButton.focus();
|
||||
}
|
||||
|
||||
function get_saved_theme() {
|
||||
let theme = null;
|
||||
try {
|
||||
theme = localStorage.getItem('mdbook-theme');
|
||||
} catch {
|
||||
// ignore error.
|
||||
}
|
||||
return theme;
|
||||
}
|
||||
|
||||
function delete_saved_theme() {
|
||||
localStorage.removeItem('mdbook-theme');
|
||||
}
|
||||
|
||||
function get_theme() {
|
||||
const theme = get_saved_theme();
|
||||
if (theme === null || theme === undefined || !themeIds.includes('mdbook-theme-' + theme)) {
|
||||
if (typeof default_dark_theme === 'undefined') {
|
||||
// A customized index.hbs might not define this, so fall back to
|
||||
// old behavior of determining the default on page load.
|
||||
return default_theme;
|
||||
}
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? default_dark_theme
|
||||
: default_light_theme;
|
||||
} else {
|
||||
return theme;
|
||||
}
|
||||
}
|
||||
|
||||
let previousTheme = default_theme;
|
||||
function set_theme(theme, store = true) {
|
||||
let ace_theme;
|
||||
|
||||
if (theme === 'coal' || theme === 'navy') {
|
||||
stylesheets.ayuHighlight.disabled = true;
|
||||
stylesheets.tomorrowNight.disabled = false;
|
||||
stylesheets.highlight.disabled = true;
|
||||
|
||||
ace_theme = 'ace/theme/tomorrow_night';
|
||||
} else if (theme === 'ayu') {
|
||||
stylesheets.ayuHighlight.disabled = false;
|
||||
stylesheets.tomorrowNight.disabled = true;
|
||||
stylesheets.highlight.disabled = true;
|
||||
ace_theme = 'ace/theme/tomorrow_night';
|
||||
} else {
|
||||
stylesheets.ayuHighlight.disabled = true;
|
||||
stylesheets.tomorrowNight.disabled = true;
|
||||
stylesheets.highlight.disabled = false;
|
||||
ace_theme = 'ace/theme/dawn';
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
themeColorMetaTag.content = getComputedStyle(document.documentElement).backgroundColor;
|
||||
}, 1);
|
||||
|
||||
if (window.ace && window.editors) {
|
||||
window.editors.forEach(function(editor) {
|
||||
editor.setTheme(ace_theme);
|
||||
});
|
||||
}
|
||||
|
||||
if (store) {
|
||||
try {
|
||||
localStorage.setItem('mdbook-theme', theme);
|
||||
} catch {
|
||||
// ignore error.
|
||||
}
|
||||
}
|
||||
|
||||
html.classList.remove(previousTheme);
|
||||
html.classList.add(theme);
|
||||
previousTheme = theme;
|
||||
updateThemeSelected();
|
||||
}
|
||||
|
||||
const query = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
query.onchange = function() {
|
||||
set_theme(get_theme(), false);
|
||||
};
|
||||
|
||||
// Set theme.
|
||||
set_theme(get_theme(), false);
|
||||
|
||||
themeToggleButton.addEventListener('click', function() {
|
||||
if (themePopup.style.display === 'block') {
|
||||
hideThemes();
|
||||
} else {
|
||||
showThemes();
|
||||
}
|
||||
});
|
||||
|
||||
themePopup.addEventListener('click', function(e) {
|
||||
let theme;
|
||||
if (e.target.className === 'theme') {
|
||||
theme = e.target.id;
|
||||
} else if (e.target.parentElement.className === 'theme') {
|
||||
theme = e.target.parentElement.id;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
theme = theme.replace(/^mdbook-theme-/, '');
|
||||
|
||||
if (theme === 'default_theme' || theme === null) {
|
||||
delete_saved_theme();
|
||||
set_theme(get_theme(), false);
|
||||
} else {
|
||||
set_theme(theme);
|
||||
}
|
||||
});
|
||||
|
||||
themePopup.addEventListener('focusout', function(e) {
|
||||
// e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
|
||||
if (!!e.relatedTarget &&
|
||||
!themeToggleButton.contains(e.relatedTarget) &&
|
||||
!themePopup.contains(e.relatedTarget)
|
||||
) {
|
||||
hideThemes();
|
||||
}
|
||||
});
|
||||
|
||||
// Should not be needed, but it works around an issue on macOS & iOS:
|
||||
// https://github.com/rust-lang/mdBook/issues/628
|
||||
document.addEventListener('click', function(e) {
|
||||
if (themePopup.style.display === 'block' &&
|
||||
!themeToggleButton.contains(e.target) &&
|
||||
!themePopup.contains(e.target)
|
||||
) {
|
||||
hideThemes();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {
|
||||
return;
|
||||
}
|
||||
if (!themePopup.contains(e.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let li;
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
hideThemes();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
li = document.activeElement.parentElement;
|
||||
if (li && li.previousElementSibling) {
|
||||
li.previousElementSibling.querySelector('button').focus();
|
||||
}
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
li = document.activeElement.parentElement;
|
||||
if (li && li.nextElementSibling) {
|
||||
li.nextElementSibling.querySelector('button').focus();
|
||||
}
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
themePopup.querySelector('li:first-child button').focus();
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
themePopup.querySelector('li:last-child button').focus();
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function sidebar() {
|
||||
const sidebar = document.getElementById('mdbook-sidebar');
|
||||
const sidebarLinks = document.querySelectorAll('#mdbook-sidebar a');
|
||||
const sidebarToggleButton = document.getElementById('mdbook-sidebar-toggle');
|
||||
const sidebarResizeHandle = document.getElementById('mdbook-sidebar-resize-handle');
|
||||
const sidebarCheckbox = document.getElementById('mdbook-sidebar-toggle-anchor');
|
||||
let firstContact = null;
|
||||
|
||||
|
||||
/* Because we cannot change the `display` using only CSS after/before the transition, we
|
||||
need JS to do it. We change the display to prevent the browsers search to find text inside
|
||||
the collapsed sidebar. */
|
||||
if (!document.documentElement.classList.contains('sidebar-visible')) {
|
||||
sidebar.style.display = 'none';
|
||||
}
|
||||
sidebar.addEventListener('transitionend', () => {
|
||||
/* We only change the display to "none" if we're collapsing the sidebar. */
|
||||
if (!sidebarCheckbox.checked) {
|
||||
sidebar.style.display = 'none';
|
||||
}
|
||||
});
|
||||
sidebarToggleButton.addEventListener('click', () => {
|
||||
/* To allow the sidebar expansion animation, we first need to put back the display. */
|
||||
if (!sidebarCheckbox.checked) {
|
||||
sidebar.style.display = '';
|
||||
// Workaround for Safari skipping the animation when changing
|
||||
// `display` and a transform in the same event loop. This forces a
|
||||
// reflow after updating the display.
|
||||
sidebar.offsetHeight;
|
||||
}
|
||||
});
|
||||
|
||||
function showSidebar() {
|
||||
document.documentElement.classList.add('sidebar-visible');
|
||||
Array.from(sidebarLinks).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', 0);
|
||||
});
|
||||
sidebarToggleButton.setAttribute('aria-expanded', true);
|
||||
sidebar.setAttribute('aria-hidden', false);
|
||||
try {
|
||||
localStorage.setItem('mdbook-sidebar', 'visible');
|
||||
} catch {
|
||||
// Ignore error.
|
||||
}
|
||||
}
|
||||
|
||||
function hideSidebar() {
|
||||
document.documentElement.classList.remove('sidebar-visible');
|
||||
Array.from(sidebarLinks).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', -1);
|
||||
});
|
||||
sidebarToggleButton.setAttribute('aria-expanded', false);
|
||||
sidebar.setAttribute('aria-hidden', true);
|
||||
try {
|
||||
localStorage.setItem('mdbook-sidebar', 'hidden');
|
||||
} catch {
|
||||
// Ignore error.
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle sidebar
|
||||
sidebarCheckbox.addEventListener('change', function sidebarToggle() {
|
||||
if (sidebarCheckbox.checked) {
|
||||
const current_width = parseInt(
|
||||
document.documentElement.style.getPropertyValue('--sidebar-target-width'), 10);
|
||||
if (current_width < 150) {
|
||||
document.documentElement.style.setProperty('--sidebar-target-width', '150px');
|
||||
}
|
||||
showSidebar();
|
||||
} else {
|
||||
hideSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
sidebarResizeHandle.addEventListener('mousedown', initResize, false);
|
||||
|
||||
function initResize() {
|
||||
window.addEventListener('mousemove', resize, false);
|
||||
window.addEventListener('mouseup', stopResize, false);
|
||||
document.documentElement.classList.add('sidebar-resizing');
|
||||
}
|
||||
function resize(e) {
|
||||
let pos = e.clientX - sidebar.offsetLeft;
|
||||
if (pos < 20) {
|
||||
hideSidebar();
|
||||
} else {
|
||||
if (!document.documentElement.classList.contains('sidebar-visible')) {
|
||||
showSidebar();
|
||||
}
|
||||
pos = Math.min(pos, window.innerWidth - 100);
|
||||
document.documentElement.style.setProperty('--sidebar-target-width', pos + 'px');
|
||||
}
|
||||
}
|
||||
//on mouseup remove windows functions mousemove & mouseup
|
||||
function stopResize() {
|
||||
document.documentElement.classList.remove('sidebar-resizing');
|
||||
window.removeEventListener('mousemove', resize, false);
|
||||
window.removeEventListener('mouseup', stopResize, false);
|
||||
}
|
||||
|
||||
document.addEventListener('touchstart', function(e) {
|
||||
firstContact = {
|
||||
x: e.touches[0].clientX,
|
||||
time: Date.now(),
|
||||
};
|
||||
}, { passive: true });
|
||||
|
||||
document.addEventListener('touchmove', function(e) {
|
||||
if (!firstContact) {
|
||||
return;
|
||||
}
|
||||
|
||||
const curX = e.touches[0].clientX;
|
||||
const xDiff = curX - firstContact.x,
|
||||
tDiff = Date.now() - firstContact.time;
|
||||
|
||||
if (tDiff < 250 && Math.abs(xDiff) >= 150) {
|
||||
if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) {
|
||||
showSidebar();
|
||||
} else if (xDiff < 0 && curX < 300) {
|
||||
hideSidebar();
|
||||
}
|
||||
|
||||
firstContact = null;
|
||||
}
|
||||
}, { passive: true });
|
||||
})();
|
||||
|
||||
(function chapterNavigation() {
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.altKey || e.ctrlKey || e.metaKey) {
|
||||
return;
|
||||
}
|
||||
if (window.search && window.search.hasFocus()) {
|
||||
return;
|
||||
}
|
||||
const html = document.querySelector('html');
|
||||
|
||||
function next() {
|
||||
const nextButton = document.querySelector('.nav-chapters.next');
|
||||
if (nextButton) {
|
||||
window.location.href = nextButton.href;
|
||||
}
|
||||
}
|
||||
function prev() {
|
||||
const previousButton = document.querySelector('.nav-chapters.previous');
|
||||
if (previousButton) {
|
||||
window.location.href = previousButton.href;
|
||||
}
|
||||
}
|
||||
function showHelp() {
|
||||
const container = document.getElementById('mdbook-help-container');
|
||||
const overlay = document.getElementById('mdbook-help-popup');
|
||||
container.style.display = 'flex';
|
||||
|
||||
// Clicking outside the popup will dismiss it.
|
||||
const mouseHandler = event => {
|
||||
if (overlay.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
document.removeEventListener('mousedown', mouseHandler);
|
||||
hideHelp();
|
||||
};
|
||||
|
||||
// Pressing esc will dismiss the popup.
|
||||
const escapeKeyHandler = event => {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
document.removeEventListener('keydown', escapeKeyHandler, true);
|
||||
hideHelp();
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', escapeKeyHandler, true);
|
||||
document.getElementById('mdbook-help-container')
|
||||
.addEventListener('mousedown', mouseHandler);
|
||||
}
|
||||
function hideHelp() {
|
||||
document.getElementById('mdbook-help-container').style.display = 'none';
|
||||
}
|
||||
|
||||
// Usually needs the Shift key to be pressed
|
||||
switch (e.key) {
|
||||
case '?':
|
||||
e.preventDefault();
|
||||
showHelp();
|
||||
break;
|
||||
}
|
||||
|
||||
// Rest of the keys are only active when the Shift key is not pressed
|
||||
if (e.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowRight':
|
||||
e.preventDefault();
|
||||
if (html.dir === 'rtl') {
|
||||
prev();
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
e.preventDefault();
|
||||
if (html.dir === 'rtl') {
|
||||
next();
|
||||
} else {
|
||||
prev();
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function clipboard() {
|
||||
const clipButtons = document.querySelectorAll('.clip-button');
|
||||
|
||||
function hideTooltip(elem) {
|
||||
elem.firstChild.innerText = '';
|
||||
elem.className = 'clip-button';
|
||||
}
|
||||
|
||||
function showTooltip(elem, msg) {
|
||||
elem.firstChild.innerText = msg;
|
||||
elem.className = 'clip-button tooltipped';
|
||||
}
|
||||
|
||||
const clipboardSnippets = new ClipboardJS('.clip-button', {
|
||||
text: function(trigger) {
|
||||
hideTooltip(trigger);
|
||||
const playground = trigger.closest('pre');
|
||||
return playground_text(playground, false);
|
||||
},
|
||||
});
|
||||
|
||||
Array.from(clipButtons).forEach(function(clipButton) {
|
||||
clipButton.addEventListener('mouseout', function(e) {
|
||||
hideTooltip(e.currentTarget);
|
||||
});
|
||||
});
|
||||
|
||||
clipboardSnippets.on('success', function(e) {
|
||||
e.clearSelection();
|
||||
showTooltip(e.trigger, 'Copied!');
|
||||
});
|
||||
|
||||
clipboardSnippets.on('error', function(e) {
|
||||
showTooltip(e.trigger, 'Clipboard error!');
|
||||
});
|
||||
})();
|
||||
|
||||
(function scrollToTop() {
|
||||
const menuTitle = document.querySelector('.menu-title');
|
||||
|
||||
menuTitle.addEventListener('click', function() {
|
||||
document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
})();
|
||||
|
||||
(function controllMenu() {
|
||||
const menu = document.getElementById('mdbook-menu-bar');
|
||||
|
||||
(function controllPosition() {
|
||||
let scrollTop = document.scrollingElement.scrollTop;
|
||||
let prevScrollTop = scrollTop;
|
||||
const minMenuY = -menu.clientHeight - 50;
|
||||
// When the script loads, the page can be at any scroll (e.g. if you refresh it).
|
||||
menu.style.top = scrollTop + 'px';
|
||||
// Same as parseInt(menu.style.top.slice(0, -2), but faster
|
||||
let topCache = menu.style.top.slice(0, -2);
|
||||
menu.classList.remove('sticky');
|
||||
let stickyCache = false; // Same as menu.classList.contains('sticky'), but faster
|
||||
document.addEventListener('scroll', function() {
|
||||
scrollTop = Math.max(document.scrollingElement.scrollTop, 0);
|
||||
// `null` means that it doesn't need to be updated
|
||||
let nextSticky = null;
|
||||
let nextTop = null;
|
||||
const scrollDown = scrollTop > prevScrollTop;
|
||||
const menuPosAbsoluteY = topCache - scrollTop;
|
||||
if (scrollDown) {
|
||||
nextSticky = false;
|
||||
if (menuPosAbsoluteY > 0) {
|
||||
nextTop = prevScrollTop;
|
||||
}
|
||||
} else {
|
||||
if (menuPosAbsoluteY > 0) {
|
||||
nextSticky = true;
|
||||
} else if (menuPosAbsoluteY < minMenuY) {
|
||||
nextTop = prevScrollTop + minMenuY;
|
||||
}
|
||||
}
|
||||
if (nextSticky === true && stickyCache === false) {
|
||||
menu.classList.add('sticky');
|
||||
stickyCache = true;
|
||||
} else if (nextSticky === false && stickyCache === true) {
|
||||
menu.classList.remove('sticky');
|
||||
stickyCache = false;
|
||||
}
|
||||
if (nextTop !== null) {
|
||||
menu.style.top = nextTop + 'px';
|
||||
topCache = nextTop;
|
||||
}
|
||||
prevScrollTop = scrollTop;
|
||||
}, { passive: true });
|
||||
})();
|
||||
(function controllBorder() {
|
||||
function updateBorder() {
|
||||
if (menu.offsetTop === 0) {
|
||||
menu.classList.remove('bordered');
|
||||
} else {
|
||||
menu.classList.add('bordered');
|
||||
}
|
||||
}
|
||||
updateBorder();
|
||||
document.addEventListener('scroll', updateBorder, { passive: true });
|
||||
})();
|
||||
})();
|
||||
756
theme/css/chrome.css
Normal file
756
theme/css/chrome.css
Normal file
@@ -0,0 +1,756 @@
|
||||
/* CSS for UI elements (a.k.a. chrome) */
|
||||
|
||||
html {
|
||||
scrollbar-color: var(--scrollbar) transparent;
|
||||
}
|
||||
#mdbook-searchresults a,
|
||||
.content a:link,
|
||||
a:visited,
|
||||
a > .hljs {
|
||||
color: var(--links);
|
||||
}
|
||||
|
||||
/*
|
||||
mdbook-body-container is necessary because mobile browsers don't seem to like
|
||||
overflow-x on the body tag when there is a <meta name="viewport"> tag.
|
||||
*/
|
||||
#mdbook-body-container {
|
||||
/*
|
||||
This is used when the sidebar pushes the body content off the side of
|
||||
the screen on small screens. Without it, dragging on mobile Safari
|
||||
will want to reposition the viewport in a weird way.
|
||||
*/
|
||||
overflow-x: clip;
|
||||
}
|
||||
|
||||
/* Menu Bar */
|
||||
|
||||
#mdbook-menu-bar,
|
||||
#mdbook-menu-bar-hover-placeholder {
|
||||
z-index: 101;
|
||||
margin: auto calc(0px - var(--page-padding));
|
||||
}
|
||||
#mdbook-menu-bar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
background-color: var(--bg);
|
||||
border-block-end-color: var(--bg);
|
||||
border-block-end-width: 1px;
|
||||
border-block-end-style: solid;
|
||||
}
|
||||
#mdbook-menu-bar.sticky,
|
||||
#mdbook-menu-bar-hover-placeholder:hover + #mdbook-menu-bar,
|
||||
#mdbook-menu-bar:hover,
|
||||
html.sidebar-visible #mdbook-menu-bar {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0 !important;
|
||||
}
|
||||
#mdbook-menu-bar-hover-placeholder {
|
||||
position: sticky;
|
||||
position: -webkit-sticky;
|
||||
top: 0;
|
||||
height: var(--menu-bar-height);
|
||||
}
|
||||
#mdbook-menu-bar.bordered {
|
||||
border-block-end-color: var(--table-border-color);
|
||||
}
|
||||
#mdbook-menu-bar .fa-svg, #mdbook-menu-bar .icon-button {
|
||||
position: relative;
|
||||
padding: 0 8px;
|
||||
z-index: 10;
|
||||
line-height: var(--menu-bar-height);
|
||||
cursor: pointer;
|
||||
transition: color 0.5s;
|
||||
}
|
||||
@media only screen and (max-width: 420px) {
|
||||
#mdbook-menu-bar .fa-svg, #mdbook-menu-bar .icon-button {
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
.icon-button .fa-svg {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.right-buttons {
|
||||
margin: 0 15px;
|
||||
}
|
||||
.right-buttons a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.left-buttons {
|
||||
display: flex;
|
||||
margin: 0 5px;
|
||||
}
|
||||
html:not(.js) .left-buttons button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
display: inline-block;
|
||||
font-weight: 200;
|
||||
font-size: 2.4rem;
|
||||
line-height: var(--menu-bar-height);
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.menu-title {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-bar,
|
||||
.menu-bar:visited,
|
||||
.nav-chapters,
|
||||
.nav-chapters:visited,
|
||||
.mobile-nav-chapters,
|
||||
.mobile-nav-chapters:visited,
|
||||
.menu-bar .icon-button,
|
||||
.menu-bar a .fa-svg {
|
||||
color: var(--icons);
|
||||
}
|
||||
|
||||
.menu-bar .fa-svg:hover,
|
||||
.menu-bar .icon-button:hover,
|
||||
.nav-chapters:hover,
|
||||
.mobile-nav-chapters .fa-svg:hover {
|
||||
color: var(--icons-hover);
|
||||
}
|
||||
|
||||
/* Nav Icons */
|
||||
|
||||
.nav-chapters {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
max-width: 150px;
|
||||
min-width: 90px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
transition: color 0.5s, background-color 0.5s;
|
||||
}
|
||||
|
||||
.nav-chapters:hover {
|
||||
text-decoration: none;
|
||||
background-color: var(--theme-hover);
|
||||
transition: background-color 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
margin-block-start: 50px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-nav-chapters {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
width: 90px;
|
||||
border-radius: 5px;
|
||||
background-color: var(--sidebar-bg);
|
||||
}
|
||||
|
||||
/* Only Firefox supports flow-relative values */
|
||||
.previous { float: left; }
|
||||
[dir=rtl] .previous { float: right; }
|
||||
|
||||
/* Only Firefox supports flow-relative values */
|
||||
.next {
|
||||
float: right;
|
||||
right: var(--page-padding);
|
||||
}
|
||||
[dir=rtl] .next {
|
||||
float: left;
|
||||
right: unset;
|
||||
left: var(--page-padding);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1080px) {
|
||||
.nav-wide-wrapper { display: none; }
|
||||
.nav-wrapper { display: block; }
|
||||
}
|
||||
|
||||
/* sidebar-visible */
|
||||
@media only screen and (max-width: 1380px) {
|
||||
#mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper .nav-wide-wrapper { display: none; }
|
||||
#mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper .nav-wrapper { display: block; }
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
|
||||
:not(pre) > .hljs {
|
||||
display: inline;
|
||||
padding: 0.1em 0.3em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
:not(pre):not(a) > .hljs {
|
||||
color: var(--inline-code-color);
|
||||
overflow-x: initial;
|
||||
}
|
||||
|
||||
a:hover > .hljs {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
pre {
|
||||
position: relative;
|
||||
}
|
||||
pre > .buttons {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 0px;
|
||||
top: 2px;
|
||||
margin: 0px;
|
||||
padding: 2px 0px;
|
||||
|
||||
color: var(--sidebar-fg);
|
||||
cursor: pointer;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: visibility 0.1s linear, opacity 0.1s linear;
|
||||
}
|
||||
pre:hover > .buttons {
|
||||
visibility: visible;
|
||||
opacity: 1
|
||||
}
|
||||
pre > .buttons :hover {
|
||||
color: var(--sidebar-active);
|
||||
border-color: var(--icons-hover);
|
||||
background-color: var(--theme-hover);
|
||||
}
|
||||
pre > .buttons button {
|
||||
cursor: inherit;
|
||||
margin: 0px 5px;
|
||||
padding: 2px 3px 0px 4px;
|
||||
font-size: 23px;
|
||||
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: 4px;
|
||||
border-color: var(--icons);
|
||||
background-color: var(--theme-popup-bg);
|
||||
transition: 100ms;
|
||||
transition-property: color,border-color,background-color;
|
||||
color: var(--icons);
|
||||
}
|
||||
|
||||
pre > .buttons button.clip-button {
|
||||
padding: 2px 4px 0px 6px;
|
||||
}
|
||||
pre > .buttons button.clip-button::before {
|
||||
/* clipboard image from octicons (https://github.com/primer/octicons/tree/v2.0.0) MIT license
|
||||
*/
|
||||
content: url('data:image/svg+xml,<svg width="21" height="20" viewBox="0 0 24 25" \
|
||||
xmlns="http://www.w3.org/2000/svg" aria-label="Copy to clipboard">\
|
||||
<path d="M18 20h2v3c0 1-1 2-2 2H2c-.998 0-2-1-2-2V5c0-.911.755-1.667 1.667-1.667h5A3.323 3.323 0 \
|
||||
0110 0a3.323 3.323 0 013.333 3.333h5C19.245 3.333 20 4.09 20 5v8.333h-2V9H2v14h16v-3zM3 \
|
||||
7h14c0-.911-.793-1.667-1.75-1.667H13.5c-.957 0-1.75-.755-1.75-1.666C11.75 2.755 10.957 2 10 \
|
||||
2s-1.75.755-1.75 1.667c0 .911-.793 1.666-1.75 1.666H4.75C3.793 5.333 3 6.09 3 7z"/>\
|
||||
<path d="M4 19h6v2H4zM12 11H4v2h8zM4 17h4v-2H4zM15 15v-3l-4.5 4.5L15 21v-3l8.027-.032L23 15z"/>\
|
||||
</svg>');
|
||||
filter: var(--copy-button-filter);
|
||||
}
|
||||
pre > .buttons button.clip-button:hover::before {
|
||||
filter: var(--copy-button-filter-hover);
|
||||
}
|
||||
|
||||
@media (pointer: coarse) {
|
||||
pre > .buttons button {
|
||||
/* On mobile, make it easier to tap buttons. */
|
||||
padding: 0.3rem 1rem;
|
||||
}
|
||||
|
||||
.sidebar-resize-indicator {
|
||||
/* Hide resize indicator on devices with limited accuracy */
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
pre > code {
|
||||
display: block;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
/* FIXME: ACE editors overlap their buttons because ACE does absolute
|
||||
positioning within the code block which breaks padding. The only solution I
|
||||
can think of is to move the padding to the outer pre tag (or insert a div
|
||||
wrapper), but that would require fixing a whole bunch of CSS rules.
|
||||
*/
|
||||
.hljs.ace_editor {
|
||||
padding: 0rem 0rem;
|
||||
}
|
||||
|
||||
pre > .result {
|
||||
margin-block-start: 10px;
|
||||
}
|
||||
|
||||
/* Search */
|
||||
|
||||
#mdbook-searchresults a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
mark {
|
||||
border-radius: 2px;
|
||||
padding-block-start: 0;
|
||||
padding-block-end: 1px;
|
||||
padding-inline-start: 3px;
|
||||
padding-inline-end: 3px;
|
||||
margin-block-start: 0;
|
||||
margin-block-end: -1px;
|
||||
margin-inline-start: -3px;
|
||||
margin-inline-end: -3px;
|
||||
background-color: var(--search-mark-bg);
|
||||
transition: background-color 300ms linear;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
mark.fade-out {
|
||||
background-color: rgba(0,0,0,0) !important;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.searchbar-outer {
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
max-width: var(--content-max-width);
|
||||
}
|
||||
|
||||
#mdbook-searchbar-outer.searching #mdbook-searchbar {
|
||||
padding-right: 30px;
|
||||
}
|
||||
#mdbook-searchbar-outer .spinner-wrapper {
|
||||
display: none;
|
||||
}
|
||||
#mdbook-searchbar-outer.searching .spinner-wrapper {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.spinner-wrapper {
|
||||
--spinner-margin: 2px;
|
||||
position: absolute;
|
||||
margin-block-start: calc(var(--searchbar-margin-block-start) + var(--spinner-margin));
|
||||
right: var(--spinner-margin);
|
||||
top: 0;
|
||||
bottom: var(--spinner-margin);
|
||||
padding: 6px;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
#fa-spin {
|
||||
animation: rotating 2s linear infinite;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@keyframes rotating {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
#mdbook-searchbar {
|
||||
width: 100%;
|
||||
margin-block-start: var(--searchbar-margin-block-start);
|
||||
margin-block-end: 0;
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
padding: 10px 16px;
|
||||
transition: box-shadow 300ms ease-in-out;
|
||||
border: 1px solid var(--searchbar-border-color);
|
||||
border-radius: 3px;
|
||||
background-color: var(--searchbar-bg);
|
||||
color: var(--searchbar-fg);
|
||||
}
|
||||
#mdbook-searchbar:focus,
|
||||
#mdbook-searchbar.active {
|
||||
box-shadow: 0 0 3px var(--searchbar-shadow-color);
|
||||
}
|
||||
|
||||
.searchresults-header {
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
padding-block-start: 18px;
|
||||
padding-block-end: 0;
|
||||
padding-inline-start: 5px;
|
||||
padding-inline-end: 0;
|
||||
color: var(--searchresults-header-fg);
|
||||
}
|
||||
|
||||
.searchresults-outer {
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
max-width: var(--content-max-width);
|
||||
border-block-end: 1px dashed var(--searchresults-border-color);
|
||||
}
|
||||
|
||||
ul#mdbook-searchresults {
|
||||
list-style: none;
|
||||
padding-inline-start: 20px;
|
||||
}
|
||||
ul#mdbook-searchresults li {
|
||||
margin: 10px 0px;
|
||||
padding: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
ul#mdbook-searchresults li.focus {
|
||||
background-color: var(--searchresults-li-bg);
|
||||
}
|
||||
ul#mdbook-searchresults span.teaser {
|
||||
display: block;
|
||||
clear: both;
|
||||
margin-block-start: 5px;
|
||||
margin-block-end: 0;
|
||||
margin-inline-start: 20px;
|
||||
margin-inline-end: 0;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
ul#mdbook-searchresults span.teaser em {
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: var(--sidebar-width);
|
||||
font-size: 0.875em;
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overscroll-behavior-y: contain;
|
||||
background-color: var(--sidebar-bg);
|
||||
color: var(--sidebar-fg);
|
||||
}
|
||||
.sidebar-iframe-inner {
|
||||
--padding: 10px;
|
||||
|
||||
background-color: var(--sidebar-bg);
|
||||
padding: var(--padding);
|
||||
margin: 0;
|
||||
font-size: 1.4rem;
|
||||
color: var(--sidebar-fg);
|
||||
min-height: calc(100vh - var(--padding) * 2);
|
||||
}
|
||||
.sidebar-iframe-outer {
|
||||
border: none;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
[dir=rtl] .sidebar { left: unset; right: 0; }
|
||||
.sidebar-resizing {
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
html:not(.sidebar-resizing) .sidebar {
|
||||
transition: transform 0.3s; /* Animation: slide away */
|
||||
}
|
||||
.sidebar code {
|
||||
line-height: 2em;
|
||||
}
|
||||
.sidebar .sidebar-scrollbox {
|
||||
overflow-y: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 10px 10px;
|
||||
}
|
||||
.sidebar .sidebar-resize-handle {
|
||||
position: absolute;
|
||||
cursor: col-resize;
|
||||
width: 0;
|
||||
right: calc(var(--sidebar-resize-indicator-width) * -1);
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar-resize-handle .sidebar-resize-indicator {
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
color: var(--icons);
|
||||
margin-inline-start: var(--sidebar-resize-indicator-space);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.sidebar-resize-handle .sidebar-resize-indicator::before {
|
||||
content: "";
|
||||
width: 2px;
|
||||
height: 12px;
|
||||
border-left: dotted 2px currentColor;
|
||||
}
|
||||
.sidebar-resize-handle .sidebar-resize-indicator::after {
|
||||
content: "";
|
||||
width: 2px;
|
||||
height: 16px;
|
||||
border-left: dotted 2px currentColor;
|
||||
}
|
||||
|
||||
[dir=rtl] .sidebar .sidebar-resize-handle {
|
||||
left: calc(var(--sidebar-resize-indicator-width) * -1);
|
||||
right: unset;
|
||||
}
|
||||
.js .sidebar .sidebar-resize-handle {
|
||||
cursor: col-resize;
|
||||
width: calc(var(--sidebar-resize-indicator-width) - var(--sidebar-resize-indicator-space));
|
||||
}
|
||||
|
||||
html:not(.js) .sidebar-resize-handle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* sidebar-hidden */
|
||||
#mdbook-sidebar-toggle-anchor:not(:checked) ~ .sidebar {
|
||||
transform: translateX(calc(0px - var(--sidebar-width) - var(--sidebar-resize-indicator-width)));
|
||||
}
|
||||
[dir=rtl] #mdbook-sidebar-toggle-anchor:not(:checked) ~ .sidebar {
|
||||
transform: translateX(calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width)));
|
||||
}
|
||||
.sidebar::-webkit-scrollbar {
|
||||
background: var(--sidebar-bg);
|
||||
}
|
||||
.sidebar::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar);
|
||||
}
|
||||
|
||||
/* sidebar-visible */
|
||||
#mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper {
|
||||
transform: translateX(calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width)));
|
||||
}
|
||||
[dir=rtl] #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper {
|
||||
transform: translateX(calc(0px - var(--sidebar-width) - var(--sidebar-resize-indicator-width)));
|
||||
}
|
||||
@media only screen and (min-width: 620px) {
|
||||
#mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper {
|
||||
transform: none;
|
||||
margin-inline-start: calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width));
|
||||
}
|
||||
[dir=rtl] #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.chapter {
|
||||
list-style: none outside none;
|
||||
padding-inline-start: 0;
|
||||
line-height: 2.2em;
|
||||
}
|
||||
|
||||
.chapter li {
|
||||
color: var(--sidebar-non-existant);
|
||||
}
|
||||
|
||||
/* This is a span wrapping the chapter link and the fold chevron. */
|
||||
.chapter-link-wrapper {
|
||||
/* Used to position the chevron to the right, allowing the text to wrap before it. */
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.chapter li a {
|
||||
/* Remove underlines. */
|
||||
text-decoration: none;
|
||||
color: var(--sidebar-fg);
|
||||
}
|
||||
|
||||
.chapter li a:hover {
|
||||
color: var(--sidebar-active);
|
||||
}
|
||||
|
||||
.chapter li a.active {
|
||||
color: var(--sidebar-active);
|
||||
}
|
||||
|
||||
/* This is the toggle chevron. */
|
||||
.chapter-fold-toggle {
|
||||
cursor: pointer;
|
||||
/* Positions the chevron to the side. */
|
||||
margin-inline-start: auto;
|
||||
padding: 0 10px;
|
||||
user-select: none;
|
||||
opacity: 0.68;
|
||||
}
|
||||
|
||||
.chapter-fold-toggle div {
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
|
||||
/* collapse the section */
|
||||
.chapter li:not(.expanded) > ol {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chapter li.chapter-item {
|
||||
line-height: 1.5em;
|
||||
margin-block-start: 0.6em;
|
||||
}
|
||||
|
||||
/* When expanded, rotate the chevron to point down. */
|
||||
.chapter li.expanded > span > .chapter-fold-toggle div {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.chapter a.current-header {
|
||||
color: var(--sidebar-active);
|
||||
}
|
||||
|
||||
.on-this-page {
|
||||
margin-left: 22px;
|
||||
border-inline-start: 4px solid var(--sidebar-header-border-color);
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.on-this-page > ol {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
/* Horizontal line in chapter list. */
|
||||
.spacer {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
.chapter .spacer {
|
||||
background-color: var(--sidebar-spacer);
|
||||
}
|
||||
|
||||
/* On touch devices, add more vertical spacing to make it easier to tap links. */
|
||||
@media (-moz-touch-enabled: 1), (pointer: coarse) {
|
||||
.chapter li a { padding: 5px 0; }
|
||||
.spacer { margin: 10px 0; }
|
||||
}
|
||||
|
||||
.section {
|
||||
list-style: none outside none;
|
||||
padding-inline-start: 20px;
|
||||
line-height: 1.9em;
|
||||
}
|
||||
|
||||
/* Theme Menu Popup */
|
||||
|
||||
.theme-popup {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: var(--menu-bar-height);
|
||||
z-index: 1000;
|
||||
border-radius: 4px;
|
||||
font-size: 0.7em;
|
||||
color: var(--fg);
|
||||
background: var(--theme-popup-bg);
|
||||
border: 1px solid var(--theme-popup-border);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: none;
|
||||
/* Don't let the children's background extend past the rounded corners. */
|
||||
overflow: hidden;
|
||||
}
|
||||
[dir=rtl] .theme-popup { left: unset; right: 10px; }
|
||||
.theme-popup .default {
|
||||
color: var(--icons);
|
||||
}
|
||||
.theme-popup .theme {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 2px 20px;
|
||||
line-height: 25px;
|
||||
white-space: nowrap;
|
||||
text-align: start;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
background: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
.theme-popup .theme:hover {
|
||||
background-color: var(--theme-hover);
|
||||
}
|
||||
|
||||
.theme-selected::before {
|
||||
display: inline-block;
|
||||
content: "✓";
|
||||
margin-inline-start: -14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
/* The container for the help popup that covers the whole window. */
|
||||
#mdbook-help-container {
|
||||
/* Position and size for the whole window. */
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
/* This uses flex layout (which is set in book.js), and centers the popup
|
||||
in the window.*/
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
/* Dim out the book while the popup is visible. */
|
||||
background: var(--overlay-bg);
|
||||
}
|
||||
|
||||
/* The popup help box. */
|
||||
#mdbook-help-popup {
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.15);
|
||||
min-width: 300px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
border-width: 1px;
|
||||
border-color: var(--theme-popup-border);
|
||||
border-style: solid;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mdbook-help-title {
|
||||
text-align: center;
|
||||
/* mdbook's margin for h2 is way too large. */
|
||||
margin: 10px;
|
||||
}
|
||||
408
theme/css/general.css
Normal file
408
theme/css/general.css
Normal file
@@ -0,0 +1,408 @@
|
||||
/* Base styles and content styles */
|
||||
|
||||
:root {
|
||||
/* Browser default font-size is 16px, this way 1 rem = 10px */
|
||||
font-size: 62.5%;
|
||||
color-scheme: var(--color-scheme);
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: "Inter", sans-serif;
|
||||
color: var(--fg);
|
||||
background-color: var(--bg);
|
||||
text-size-adjust: none;
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-size: 1.6rem;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--mono-font) !important;
|
||||
font-size: var(--code-font-size);
|
||||
direction: ltr !important;
|
||||
}
|
||||
|
||||
/* make long words/inline code not x overflow */
|
||||
main {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* make wide tables scroll if they overflow */
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* Don't change font size in headers. */
|
||||
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
|
||||
font-size: unset;
|
||||
}
|
||||
|
||||
.left { float: left; }
|
||||
.right { float: right; }
|
||||
.boring { opacity: 0.6; }
|
||||
.hide-boring .boring { display: none; }
|
||||
.hidden { display: none !important; }
|
||||
|
||||
h2, h3 { margin-block-start: 2.5em; }
|
||||
h4, h5 { margin-block-start: 2em; }
|
||||
|
||||
.header + .header h3,
|
||||
.header + .header h4,
|
||||
.header + .header h5 {
|
||||
margin-block-start: 1em;
|
||||
}
|
||||
|
||||
h1:target::before,
|
||||
h2:target::before,
|
||||
h3:target::before,
|
||||
h4:target::before,
|
||||
h5:target::before,
|
||||
h6:target::before,
|
||||
dt:target::before {
|
||||
display: inline-block;
|
||||
content: "»";
|
||||
margin-inline-start: -30px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
/* This is broken on Safari as of version 14, but is fixed
|
||||
in Safari Technology Preview 117 which I think will be Safari 14.2.
|
||||
https://bugs.webkit.org/show_bug.cgi?id=218076
|
||||
*/
|
||||
:target {
|
||||
/* Safari does not support logical properties */
|
||||
scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);
|
||||
}
|
||||
|
||||
.page {
|
||||
outline: 0;
|
||||
padding: 0 var(--page-padding);
|
||||
margin-block-start: calc(0px - var(--menu-bar-height)); /* Compensate for the #mdbook-menu-bar-hover-placeholder */
|
||||
}
|
||||
.page-wrapper {
|
||||
box-sizing: border-box;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
html:not(.js) .page-wrapper,
|
||||
.js:not(.sidebar-resizing) .page-wrapper {
|
||||
transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */
|
||||
}
|
||||
[dir=rtl]:not(.js) .page-wrapper,
|
||||
[dir=rtl].js:not(.sidebar-resizing) .page-wrapper {
|
||||
transition: margin-right 0.3s ease, transform 0.3s ease; /* Animation: slide away */
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow-y: auto;
|
||||
padding: 0 5px 50px 5px;
|
||||
}
|
||||
.content main {
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
max-width: var(--content-max-width);
|
||||
}
|
||||
.content p { line-height: 1.45em; }
|
||||
.content ol { line-height: 1.45em; }
|
||||
.content ul { line-height: 1.45em; }
|
||||
.content a { text-decoration: none; }
|
||||
.content a:hover { text-decoration: underline; }
|
||||
.content img, .content video { max-width: 100%; }
|
||||
.content .header:link,
|
||||
.content .header:visited {
|
||||
color: var(--fg);
|
||||
}
|
||||
.content .header:link,
|
||||
.content .header:visited:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 0 auto;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table td {
|
||||
padding: 3px 20px;
|
||||
border: 1px var(--table-border-color) solid;
|
||||
}
|
||||
table thead {
|
||||
background: var(--table-header-bg);
|
||||
}
|
||||
table thead td {
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
}
|
||||
table thead th {
|
||||
padding: 3px 20px;
|
||||
}
|
||||
table thead tr {
|
||||
border: 1px var(--table-header-bg) solid;
|
||||
}
|
||||
/* Alternate background colors for rows */
|
||||
table tbody tr:nth-child(2n) {
|
||||
background: var(--table-alternate-bg);
|
||||
}
|
||||
|
||||
|
||||
blockquote {
|
||||
margin: 20px 0;
|
||||
padding: 0 20px;
|
||||
color: var(--fg);
|
||||
background-color: var(--quote-bg);
|
||||
border-block-start: .1em solid var(--quote-border);
|
||||
border-block-end: .1em solid var(--quote-border);
|
||||
}
|
||||
|
||||
/* TODO: Remove .warning in a future version of mdbook, it is replaced by
|
||||
blockquote tags. */
|
||||
.warning {
|
||||
margin: 20px;
|
||||
padding: 0 20px;
|
||||
border-inline-start: 2px solid var(--warning-border);
|
||||
}
|
||||
|
||||
.warning:before {
|
||||
position: absolute;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-inline-start: calc(-1.5rem - 21px);
|
||||
content: "ⓘ";
|
||||
text-align: center;
|
||||
background-color: var(--bg);
|
||||
color: var(--warning-border);
|
||||
font-weight: bold;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
blockquote .warning:before {
|
||||
background-color: var(--quote-bg);
|
||||
}
|
||||
|
||||
kbd {
|
||||
background-color: var(--table-border-color);
|
||||
border-radius: 4px;
|
||||
border: solid 1px var(--theme-popup-border);
|
||||
box-shadow: inset 0 -1px 0 var(--theme-hover);
|
||||
display: inline-block;
|
||||
font-size: var(--code-font-size);
|
||||
font-family: var(--mono-font);
|
||||
line-height: 10px;
|
||||
padding: 4px 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
sup {
|
||||
/* Set the line-height for superscript and footnote references so that there
|
||||
isn't an awkward space appearing above lines that contain the footnote.
|
||||
|
||||
See https://github.com/rust-lang/mdBook/pull/2443#discussion_r1813773583
|
||||
for an explanation.
|
||||
*/
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.footnote-definition {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
/* The default spacing for a list is a little too large. */
|
||||
.footnote-definition ul,
|
||||
.footnote-definition ol {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.footnote-definition > li {
|
||||
/* Required to position the ::before target */
|
||||
position: relative;
|
||||
}
|
||||
.footnote-definition > li:target {
|
||||
scroll-margin-top: 50vh;
|
||||
}
|
||||
.footnote-reference:target {
|
||||
scroll-margin-top: 50vh;
|
||||
}
|
||||
/* Draws a border around the footnote (including the marker) when it is selected.
|
||||
TODO: If there are multiple linkbacks, highlight which one you just came
|
||||
from so you know which one to click.
|
||||
*/
|
||||
.footnote-definition > li:target::before {
|
||||
border: 2px solid var(--footnote-highlight);
|
||||
border-radius: 6px;
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
bottom: -8px;
|
||||
left: -32px;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
}
|
||||
/* Pulses the footnote reference so you can quickly see where you left off reading.
|
||||
This could use some improvement.
|
||||
*/
|
||||
@media not (prefers-reduced-motion) {
|
||||
.footnote-reference:target {
|
||||
animation: fn-highlight 0.8s;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
@keyframes fn-highlight {
|
||||
from {
|
||||
background-color: var(--footnote-highlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tooltiptext {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
color: #fff;
|
||||
background-color: #333;
|
||||
transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */
|
||||
left: -8px; /* Half of the width of the icon */
|
||||
top: -35px;
|
||||
font-size: 0.8em;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px 8px;
|
||||
margin: 5px;
|
||||
z-index: 1000;
|
||||
}
|
||||
.tooltipped .tooltiptext {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.chapter li.part-title {
|
||||
color: var(--sidebar-fg);
|
||||
margin: 5px 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.result-no-output {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.fa-svg svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
fill: currentColor;
|
||||
margin-bottom: -0.1em;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
|
||||
/* This uses a CSS counter to add numbers to definitions, but only if there is
|
||||
more than one definition. */
|
||||
dl, dt {
|
||||
counter-reset: dd-counter;
|
||||
}
|
||||
|
||||
/* When there is more than one definition, increment the counter. The first
|
||||
selector selects the first definition, and the second one selects definitions
|
||||
2 and beyond.*/
|
||||
dd:has(+ dd), dd + dd {
|
||||
counter-increment: dd-counter;
|
||||
/* Use flex display to help with positioning the numbers when there is a p
|
||||
tag inside the definition. */
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* Shows the counter for definitions. The first selector selects the first
|
||||
definition, and the second one selections definitions 2 and beyond.*/
|
||||
dd:has(+ dd)::before, dd + dd::before {
|
||||
content: counter(dd-counter) ". ";
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
dd > p {
|
||||
/* For loose definitions that have a p tag inside, don't add a bunch of
|
||||
space before the definition. */
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Remove some excess space from the bottom. */
|
||||
.blockquote-tag p:last-child {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.blockquote-tag {
|
||||
/* Add some padding to make the vertical bar a little taller than the text.*/
|
||||
padding: 2px 0px 2px 20px;
|
||||
/* Add a solid color bar on the left side. */
|
||||
border-inline-start-style: solid;
|
||||
border-inline-start-width: 4px;
|
||||
/* Disable the background color from normal blockquotes . */
|
||||
background-color: inherit;
|
||||
/* Disable border blocks from blockquotes. */
|
||||
border-block-start: none;
|
||||
border-block-end: none;
|
||||
}
|
||||
|
||||
.blockquote-tag-title svg {
|
||||
fill: currentColor;
|
||||
/* Add space between the icon and the title. */
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.blockquote-tag-note {
|
||||
border-inline-start-color: var(--blockquote-note-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-tip {
|
||||
border-inline-start-color: var(--blockquote-tip-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-important {
|
||||
border-inline-start-color: var(--blockquote-important-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-warning {
|
||||
border-inline-start-color: var(--blockquote-warning-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-caution {
|
||||
border-inline-start-color: var(--blockquote-caution-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-note .blockquote-tag-title {
|
||||
color: var(--blockquote-note-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-tip .blockquote-tag-title {
|
||||
color: var(--blockquote-tip-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-important .blockquote-tag-title {
|
||||
color: var(--blockquote-important-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-warning .blockquote-tag-title {
|
||||
color: var(--blockquote-warning-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-caution .blockquote-tag-title {
|
||||
color: var(--blockquote-caution-color);
|
||||
}
|
||||
|
||||
.blockquote-tag-title {
|
||||
/* Slightly increase the weight for more emphasis. */
|
||||
font-weight: 600;
|
||||
/* Vertically center the icon with the text. */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* Remove default large margins for a more compact display. */
|
||||
margin: 2px 0 8px 0;
|
||||
}
|
||||
|
||||
.blockquote-tag-title .fa-svg {
|
||||
fill: currentColor;
|
||||
/* Add some space between the icon and the text. */
|
||||
margin-right: 8px;
|
||||
}
|
||||
50
theme/css/print.css
Normal file
50
theme/css/print.css
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
#mdbook-sidebar,
|
||||
#mdbook-menu-bar,
|
||||
.nav-chapters,
|
||||
.mobile-nav-chapters {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mdbook-page-wrapper.page-wrapper {
|
||||
transform: none !important;
|
||||
margin-inline-start: 0px;
|
||||
overflow-y: initial;
|
||||
}
|
||||
|
||||
#mdbook-content {
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.page {
|
||||
overflow-y: initial;
|
||||
}
|
||||
|
||||
code {
|
||||
direction: ltr !important;
|
||||
}
|
||||
|
||||
pre > .buttons {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
a, a:visited, a:active, a:hover {
|
||||
color: #4183c4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
page-break-inside: avoid;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
page-break-inside: avoid;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.fa {
|
||||
display: none !important;
|
||||
}
|
||||
445
theme/css/variables.css
Normal file
445
theme/css/variables.css
Normal file
@@ -0,0 +1,445 @@
|
||||
|
||||
/* Globals */
|
||||
|
||||
:root {
|
||||
--sidebar-target-width: 300px;
|
||||
--sidebar-width: min(var(--sidebar-target-width), 80vw);
|
||||
--sidebar-resize-indicator-width: 8px;
|
||||
--sidebar-resize-indicator-space: 2px;
|
||||
--page-padding: 15px;
|
||||
--content-max-width: 750px;
|
||||
--menu-bar-height: 50px;
|
||||
--mono-font: "JetBrains Mono", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace;
|
||||
--code-font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */
|
||||
--searchbar-margin-block-start: 5px;
|
||||
}
|
||||
|
||||
/* Themes */
|
||||
|
||||
.ayu {
|
||||
--bg: hsl(210, 25%, 8%);
|
||||
--fg: #c5c5c5;
|
||||
|
||||
--sidebar-bg: #14191f;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existant: #5c6773;
|
||||
--sidebar-active: #ffb454;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
--links: #0096cf;
|
||||
|
||||
--inline-code-color: #ffb454;
|
||||
|
||||
--theme-popup-bg: #14191f;
|
||||
--theme-popup-border: #5c6773;
|
||||
--theme-hover: #191f26;
|
||||
|
||||
--quote-bg: hsl(226, 15%, 17%);
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(210, 25%, 13%);
|
||||
--table-header-bg: hsl(210, 25%, 28%);
|
||||
--table-alternate-bg: hsl(210, 25%, 11%);
|
||||
|
||||
--searchbar-border-color: #848484;
|
||||
--searchbar-bg: #424242;
|
||||
--searchbar-fg: #fff;
|
||||
--searchbar-shadow-color: #d4c89f;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #252932;
|
||||
--search-mark-bg: #e3b171;
|
||||
|
||||
--color-scheme: dark;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(45%) sepia(6%) saturate(621%) hue-rotate(198deg) brightness(99%) contrast(85%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(68%) sepia(55%) saturate(531%) hue-rotate(341deg) brightness(104%) contrast(101%);
|
||||
|
||||
--footnote-highlight: #2668a6;
|
||||
|
||||
--overlay-bg: rgba(33, 40, 48, 0.4);
|
||||
|
||||
--blockquote-note-color: #74b9ff;
|
||||
--blockquote-tip-color: #09ca09;
|
||||
--blockquote-important-color: #d3abff;
|
||||
--blockquote-warning-color: #f0b72f;
|
||||
--blockquote-caution-color: #f21424;
|
||||
|
||||
--sidebar-header-border-color: #c18639;
|
||||
}
|
||||
|
||||
.coal {
|
||||
--bg: hsl(200, 7%, 8%);
|
||||
--fg: #98a3ad;
|
||||
|
||||
--sidebar-bg: #292c2f;
|
||||
--sidebar-fg: #a1adb8;
|
||||
--sidebar-non-existant: #505254;
|
||||
--sidebar-active: #3473ad;
|
||||
--sidebar-spacer: #393939;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #43484d;
|
||||
--icons-hover: #b3c0cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #141617;
|
||||
--theme-popup-border: #43484d;
|
||||
--theme-hover: #1f2124;
|
||||
|
||||
--quote-bg: hsl(234, 21%, 18%);
|
||||
--quote-border: hsl(234, 21%, 23%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(200, 7%, 13%);
|
||||
--table-header-bg: hsl(200, 7%, 28%);
|
||||
--table-alternate-bg: hsl(200, 7%, 11%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #b7b7b7;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #98a3ad;
|
||||
--searchresults-li-bg: #2b2b2f;
|
||||
--search-mark-bg: #355c7d;
|
||||
|
||||
--color-scheme: dark;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(26%) sepia(8%) saturate(575%) hue-rotate(169deg) brightness(87%) contrast(82%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(36%) sepia(70%) saturate(503%) hue-rotate(167deg) brightness(98%) contrast(89%);
|
||||
|
||||
--footnote-highlight: #4079ae;
|
||||
|
||||
--overlay-bg: rgba(33, 40, 48, 0.4);
|
||||
|
||||
--blockquote-note-color: #4493f8;
|
||||
--blockquote-tip-color: #08ae08;
|
||||
--blockquote-important-color: #ab7df8;
|
||||
--blockquote-warning-color: #d29922;
|
||||
--blockquote-caution-color: #d91b29;
|
||||
|
||||
--sidebar-header-border-color: #3473ad;
|
||||
}
|
||||
|
||||
.light, html:not(.js) {
|
||||
--bg: hsl(0, 0%, 100%);
|
||||
--fg: hsl(0, 0%, 0%);
|
||||
|
||||
--sidebar-bg: #fafafa;
|
||||
--sidebar-fg: hsl(0, 0%, 0%);
|
||||
--sidebar-non-existant: #aaaaaa;
|
||||
--sidebar-active: #1f1fff;
|
||||
--sidebar-spacer: #f4f4f4;
|
||||
|
||||
--scrollbar: #8F8F8F;
|
||||
|
||||
--icons: #747474;
|
||||
--icons-hover: #000000;
|
||||
|
||||
--links: #20609f;
|
||||
|
||||
--inline-code-color: #301900;
|
||||
|
||||
--theme-popup-bg: #fafafa;
|
||||
--theme-popup-border: #cccccc;
|
||||
--theme-hover: #e6e6e6;
|
||||
|
||||
--quote-bg: hsl(197, 37%, 96%);
|
||||
--quote-border: hsl(197, 37%, 91%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(0, 0%, 95%);
|
||||
--table-header-bg: hsl(0, 0%, 80%);
|
||||
--table-alternate-bg: hsl(0, 0%, 97%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #fafafa;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #e4f2fe;
|
||||
--search-mark-bg: #a2cff5;
|
||||
|
||||
--color-scheme: light;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(45.49%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(14%) sepia(93%) saturate(4250%) hue-rotate(243deg) brightness(99%) contrast(130%);
|
||||
|
||||
--footnote-highlight: #7e7eff;
|
||||
|
||||
--overlay-bg: rgba(200, 200, 205, 0.4);
|
||||
|
||||
--blockquote-note-color: #0969da;
|
||||
--blockquote-tip-color: #008000;
|
||||
--blockquote-important-color: #8250df;
|
||||
--blockquote-warning-color: #9a6700;
|
||||
--blockquote-caution-color: #b52731;
|
||||
|
||||
--sidebar-header-border-color: #6e6edb;
|
||||
}
|
||||
|
||||
.nnavy {
|
||||
--bg: hsl(226, 23%, 11%);
|
||||
--fg: #bcbdd0;
|
||||
|
||||
--sidebar-bg: #282d3f;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existant: #505274;
|
||||
--sidebar-active: #2b79a2;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #161923;
|
||||
--theme-popup-border: #737480;
|
||||
--theme-hover: #282e40;
|
||||
|
||||
--quote-bg: hsl(226, 15%, 17%);
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(226, 23%, 16%);
|
||||
--table-header-bg: hsl(226, 23%, 31%);
|
||||
--table-alternate-bg: hsl(226, 23%, 14%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #aeaec6;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #5f5f71;
|
||||
--searchresults-border-color: #5c5c68;
|
||||
--searchresults-li-bg: #242430;
|
||||
--search-mark-bg: #a2cff5;
|
||||
|
||||
--color-scheme: dark;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(51%) sepia(10%) saturate(393%) hue-rotate(198deg) brightness(86%) contrast(87%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(46%) sepia(20%) saturate(1537%) hue-rotate(156deg) brightness(85%) contrast(90%);
|
||||
|
||||
--footnote-highlight: #4079ae;
|
||||
|
||||
--overlay-bg: rgba(33, 40, 48, 0.4);
|
||||
|
||||
--blockquote-note-color: #4493f8;
|
||||
--blockquote-tip-color: #09ca09;
|
||||
--blockquote-important-color: #ab7df8;
|
||||
--blockquote-warning-color: #d29922;
|
||||
--blockquote-caution-color: #f21424;
|
||||
|
||||
--sidebar-header-border-color: #2f6ab5;
|
||||
}
|
||||
|
||||
.rust {
|
||||
--bg: hsl(60, 9%, 87%);
|
||||
--fg: #262625;
|
||||
|
||||
--sidebar-bg: #3b2e2a;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existant: #505254;
|
||||
--sidebar-active: #e69f67;
|
||||
--sidebar-spacer: #45373a;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #262625;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #6e6b5e;
|
||||
|
||||
--theme-popup-bg: #e1e1db;
|
||||
--theme-popup-border: #b38f6b;
|
||||
--theme-hover: #99908a;
|
||||
|
||||
--quote-bg: hsl(60, 5%, 75%);
|
||||
--quote-border: hsl(60, 5%, 70%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(60, 9%, 82%);
|
||||
--table-header-bg: #b3a497;
|
||||
--table-alternate-bg: hsl(60, 9%, 84%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #fafafa;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #dec2a2;
|
||||
--search-mark-bg: #e69f67;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(51%) sepia(10%) saturate(393%) hue-rotate(198deg) brightness(86%) contrast(87%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(77%) sepia(16%) saturate(1798%) hue-rotate(328deg) brightness(98%) contrast(83%);
|
||||
|
||||
--footnote-highlight: #d3a17a;
|
||||
|
||||
--overlay-bg: rgba(150, 150, 150, 0.25);
|
||||
|
||||
--blockquote-note-color: #023b95;
|
||||
--blockquote-tip-color: #007700;
|
||||
--blockquote-important-color: #8250df;
|
||||
--blockquote-warning-color: #603700;
|
||||
--blockquote-caution-color: #aa1721;
|
||||
|
||||
--sidebar-header-border-color: #8c391f;
|
||||
}
|
||||
|
||||
.navy {
|
||||
--bg: #161031;
|
||||
--fg: #ffffff;
|
||||
|
||||
--sidebar-bg: #080511;
|
||||
--sidebar-fg: #bebebf;
|
||||
--sidebar-non-existant: #505274;
|
||||
--sidebar-active: #a594f9;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
--links: #a594f9;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #161923;
|
||||
--theme-popup-border: #737480;
|
||||
--theme-hover: #282e40;
|
||||
|
||||
--quote-bg: hsl(226, 15%, 17%);
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(226, 23%, 16%);
|
||||
--table-header-bg: hsl(226, 23%, 31%);
|
||||
--table-alternate-bg: hsl(226, 23%, 14%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #aeaec6;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #5f5f71;
|
||||
--searchresults-border-color: #5c5c68;
|
||||
--searchresults-li-bg: #242430;
|
||||
--search-mark-bg: #a2cff5;
|
||||
|
||||
--color-scheme: dark;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(51%) sepia(10%) saturate(393%) hue-rotate(198deg) brightness(86%) contrast(87%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(46%) sepia(20%) saturate(1537%) hue-rotate(156deg) brightness(85%) contrast(90%);
|
||||
|
||||
--footnote-highlight: #4079ae;
|
||||
|
||||
--overlay-bg: rgba(33, 40, 48, 0.4);
|
||||
|
||||
--blockquote-note-color: #4493f8;
|
||||
--blockquote-tip-color: #09ca09;
|
||||
--blockquote-important-color: #ab7df8;
|
||||
--blockquote-warning-color: #d29922;
|
||||
--blockquote-caution-color: #f21424;
|
||||
|
||||
--sidebar-header-border-color: #000000;
|
||||
}
|
||||
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html:not(.js) {
|
||||
--bg: hsl(200, 7%, 8%);
|
||||
--fg: #98a3ad;
|
||||
|
||||
--sidebar-bg: #292c2f;
|
||||
--sidebar-fg: #a1adb8;
|
||||
--sidebar-non-existant: #505254;
|
||||
--sidebar-active: #3473ad;
|
||||
--sidebar-spacer: #393939;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #43484d;
|
||||
--icons-hover: #b3c0cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #141617;
|
||||
--theme-popup-border: #43484d;
|
||||
--theme-hover: #1f2124;
|
||||
|
||||
--quote-bg: hsl(234, 21%, 18%);
|
||||
--quote-border: hsl(234, 21%, 23%);
|
||||
|
||||
--warning-border: #ff8e00;
|
||||
|
||||
--table-border-color: hsl(200, 7%, 13%);
|
||||
--table-header-bg: hsl(200, 7%, 28%);
|
||||
--table-alternate-bg: hsl(200, 7%, 11%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #b7b7b7;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #98a3ad;
|
||||
--searchresults-li-bg: #2b2b2f;
|
||||
--search-mark-bg: #355c7d;
|
||||
|
||||
--color-scheme: dark;
|
||||
|
||||
/* Same as `--icons` */
|
||||
--copy-button-filter: invert(26%) sepia(8%) saturate(575%) hue-rotate(169deg) brightness(87%) contrast(82%);
|
||||
/* Same as `--sidebar-active` */
|
||||
--copy-button-filter-hover: invert(36%) sepia(70%) saturate(503%) hue-rotate(167deg) brightness(98%) contrast(89%);
|
||||
|
||||
--footnote-highlight: #4079ae;
|
||||
|
||||
--overlay-bg: rgba(33, 40, 48, 0.4);
|
||||
|
||||
--blockquote-note-color: #4493f8;
|
||||
--blockquote-tip-color: #08ae08;
|
||||
--blockquote-important-color: #ab7df8;
|
||||
--blockquote-warning-color: #d29922;
|
||||
--blockquote-caution-color: #d91b29;
|
||||
|
||||
--sidebar-header-border-color: #3473ad;
|
||||
}
|
||||
}
|
||||
BIN
theme/favicon.png
Normal file
BIN
theme/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
52
theme/favicon.svg
Normal file
52
theme/favicon.svg
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="100.00389mm"
|
||||
height="100.00055mm"
|
||||
viewBox="0 0 100.00389 100.00055"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1">
|
||||
<linearGradient
|
||||
id="linearGradient8">
|
||||
<stop
|
||||
style="stop-color:#64ffe1;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop8" />
|
||||
<stop
|
||||
style="stop-color:#a3b9f5;stop-opacity:1;"
|
||||
offset="0.5"
|
||||
id="stop10" />
|
||||
<stop
|
||||
style="stop-color:#d1b8fb;stop-opacity:1;"
|
||||
offset="0.75"
|
||||
id="stop11" />
|
||||
<stop
|
||||
style="stop-color:#f3cdff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient8"
|
||||
id="linearGradient9"
|
||||
x1="-42.253399"
|
||||
y1="135.74071"
|
||||
x2="-41.225731"
|
||||
y2="239.96782"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.73239966,-0.77274564,0.76516532,0.73965537,-4.0932457,-8.6853091)" />
|
||||
</defs>
|
||||
<g
|
||||
id="layer1"
|
||||
transform="translate(-69.164575,-124.62841)">
|
||||
<path
|
||||
id="path1"
|
||||
style="fill:url(#linearGradient9);fill-opacity:1;stroke:#ffffff;stroke-width:0.281629;stroke-opacity:0"
|
||||
d="m 100.38267,124.7767 a 31.775753,32.090549 0 0 0 -22.257004,9.893 31.775753,32.090549 0 0 0 0.979215,45.36846 l -0.0038,0.004 45.917939,44.38672 43.95132,-46.37229 -45.91795,-44.38673 -0.004,0.004 a 31.775753,32.090549 0 0 0 -22.66587,-8.8969 z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
theme/fonts/fonts.css
Normal file
1
theme/fonts/fonts.css
Normal file
@@ -0,0 +1 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap');
|
||||
83
theme/highlight.css
Normal file
83
theme/highlight.css
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* An increased contrast highlighting scheme loosely based on the
|
||||
* "Base16 Atelier Dune Light" theme by Bram de Haan
|
||||
* (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune)
|
||||
* Original Base16 color scheme by Chris Kempson
|
||||
* (https://github.com/chriskempson/base16)
|
||||
*/
|
||||
|
||||
/* Comment */
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #575757;
|
||||
}
|
||||
|
||||
/* Red */
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-attribute,
|
||||
.hljs-attr,
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-regexp,
|
||||
.hljs-link,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #d70025;
|
||||
}
|
||||
|
||||
/* Orange */
|
||||
.hljs-number,
|
||||
.hljs-meta,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params {
|
||||
color: #b21e00;
|
||||
}
|
||||
|
||||
/* Green */
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet {
|
||||
color: #008200;
|
||||
}
|
||||
|
||||
/* Blue */
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #0030f2;
|
||||
}
|
||||
|
||||
/* Purple */
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
color: #9d00ec;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
background: #f6f7f6;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #22863a;
|
||||
background-color: #f0fff4;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #b31d28;
|
||||
background-color: #ffeef0;
|
||||
}
|
||||
2630
theme/highlight.js
Normal file
2630
theme/highlight.js
Normal file
File diff suppressed because it is too large
Load Diff
367
theme/index.hbs
Normal file
367
theme/index.hbs
Normal file
@@ -0,0 +1,367 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="{{ language }}" class="{{ default_theme }} sidebar-visible" dir="{{ text_direction }}">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ title }}</title>
|
||||
{{#if is_print }}
|
||||
<meta name="robots" content="noindex">
|
||||
{{/if}}
|
||||
{{#if base_url}}
|
||||
<base href="{{ base_url }}">
|
||||
{{/if}}
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
{{> head}}
|
||||
|
||||
<meta name="description" content="{{ description }}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
{{#if favicon_svg}}
|
||||
<link rel="icon" href="{{ resource "favicon.svg" }}">
|
||||
{{/if}}
|
||||
{{#if favicon_png}}
|
||||
<link rel="shortcut icon" href="{{ resource "favicon.png" }}">
|
||||
{{/if}}
|
||||
<link rel="stylesheet" href="{{ resource "css/variables.css" }}">
|
||||
<link rel="stylesheet" href="{{ resource "css/general.css" }}">
|
||||
<link rel="stylesheet" href="{{ resource "css/chrome.css" }}">
|
||||
{{#if print_enable}}
|
||||
<link rel="stylesheet" href="{{ resource "css/print.css" }}" media="print">
|
||||
{{/if}}
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="{{ resource "fonts/fonts.css" }}">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" id="mdbook-highlight-css" href="{{ resource "highlight.css" }}">
|
||||
<link rel="stylesheet" id="mdbook-tomorrow-night-css" href="{{ resource "tomorrow-night.css" }}">
|
||||
<link rel="stylesheet" id="mdbook-ayu-highlight-css" href="{{ resource "ayu-highlight.css" }}">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
{{#each additional_css}}
|
||||
<link rel="stylesheet" href="{{ resource this }}">
|
||||
{{/each}}
|
||||
|
||||
{{#if mathjax_support}}
|
||||
<!-- MathJax -->
|
||||
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
{{/if}}
|
||||
|
||||
<!-- Provide site root and default themes to javascript -->
|
||||
<script>
|
||||
const path_to_root = "{{ path_to_root }}";
|
||||
const default_light_theme = "{{ default_theme }}";
|
||||
const default_dark_theme = "{{ preferred_dark_theme }}";
|
||||
{{#if search_js}}
|
||||
window.path_to_searchindex_js = "{{ resource "searchindex.js" }}";
|
||||
{{/if}}
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="{{ resource "toc.js" }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mdbook-help-container">
|
||||
<div id="mdbook-help-popup">
|
||||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||||
<div>
|
||||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||||
{{#if search_enabled}}
|
||||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||||
{{/if}}
|
||||
<p>Press <kbd>?</kbd> to show this help</p>
|
||||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mdbook-body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
let theme = localStorage.getItem('mdbook-theme');
|
||||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||||
let theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('{{ default_theme }}')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="mdbook-sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
let sidebar = null;
|
||||
const sidebar_toggle = document.getElementById("mdbook-sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
sidebar_toggle.checked = false;
|
||||
}
|
||||
if (sidebar === 'visible') {
|
||||
sidebar_toggle.checked = true;
|
||||
} else {
|
||||
html.classList.remove('sidebar-visible');
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav id="mdbook-sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="{{ path_to_root }}toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="mdbook-sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="mdbook-page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
{{> header}}
|
||||
<div id="mdbook-menu-bar-hover-placeholder"></div>
|
||||
<div id="mdbook-menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar">
|
||||
{{fa "solid" "bars"}}
|
||||
</label>
|
||||
<button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list">
|
||||
{{fa "solid" "paintbrush"}}
|
||||
</button>
|
||||
<ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li>
|
||||
</ul>
|
||||
{{#if search_enabled}}
|
||||
<button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar">
|
||||
{{fa "solid" "magnifying-glass"}}
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">{{ book_title }}</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
{{#if print_enable}}
|
||||
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
|
||||
{{fa "solid" "print" "print-button"}}
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if git_repository_url}}
|
||||
<a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository">
|
||||
{{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">
|
||||
{{fa "solid" "pencil" "git-edit-button"}}
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if search_enabled}}
|
||||
<div id="mdbook-search-wrapper" class="hidden">
|
||||
<form id="mdbook-searchbar-outer" class="searchbar-outer">
|
||||
<div class="search-wrapper">
|
||||
<input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header">
|
||||
<div class="spinner-wrapper">
|
||||
{{fa "solid" "spinner" "fa-spin"}}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="mdbook-searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="mdbook-searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="mdbook-searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="mdbook-content" class="content">
|
||||
<main>
|
||||
{{{ content }}}
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
{{#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")}}
|
||||
{{fa "solid" "angle-right"}}
|
||||
{{else}}
|
||||
{{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")}}
|
||||
{{fa "solid" "angle-left"}}
|
||||
{{else}}
|
||||
{{fa "solid" "angle-right"}}
|
||||
{{/if}}
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
{{#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")}}
|
||||
{{fa "solid" "angle-right"}}
|
||||
{{else}}
|
||||
{{fa "solid" "angle-left"}}
|
||||
{{/if}}
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
{{#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")}}
|
||||
{{fa "solid" "angle-left"}}
|
||||
{{else}}
|
||||
{{fa "solid" "angle-right"}}
|
||||
{{/if}}
|
||||
</a>
|
||||
{{/if}}
|
||||
</nav>
|
||||
|
||||
</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>
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsAddress = wsProtocol + "//" + location.host + "/" + "{{{live_reload_endpoint}}}";
|
||||
const socket = new WebSocket(wsAddress);
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if playground_line_numbers}}
|
||||
<script>
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if playground_copyable}}
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if playground_js}}
|
||||
<script src="{{ resource "ace.js" }}"></script>
|
||||
<script src="{{ resource "mode-rust.js" }}"></script>
|
||||
<script src="{{ resource "editor.js" }}"></script>
|
||||
<script src="{{ resource "theme-dawn.js" }}"></script>
|
||||
<script src="{{ resource "theme-tomorrow_night.js" }}"></script>
|
||||
{{/if}}
|
||||
|
||||
{{#if search_js}}
|
||||
<script src="{{ resource "elasticlunr.min.js" }}"></script>
|
||||
<script src="{{ resource "mark.min.js" }}"></script>
|
||||
<script src="{{ resource "searcher.js" }}"></script>
|
||||
{{/if}}
|
||||
|
||||
<script src="{{ resource "clipboard.min.js" }}"></script>
|
||||
<script src="{{ resource "highlight.js" }}"></script>
|
||||
<script src="{{ resource "book.js" }}"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
{{#each additional_js}}
|
||||
<script src="{{ resource this}}"></script>
|
||||
{{/each}}
|
||||
|
||||
{{#if is_print}}
|
||||
{{#if mathjax_support}}
|
||||
<script>
|
||||
window.addEventListener('load', function() {
|
||||
MathJax.Hub.Register.StartupHook('End', function() {
|
||||
window.setTimeout(window.print, 100);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{else}}
|
||||
<script>
|
||||
window.addEventListener('load', function() {
|
||||
window.setTimeout(window.print, 100);
|
||||
});
|
||||
</script>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if fragment_map}}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const fragmentMap =
|
||||
{{{fragment_map}}}
|
||||
;
|
||||
const target = fragmentMap[window.location.hash];
|
||||
if (target) {
|
||||
let url = new URL(target, window.location.href);
|
||||
window.location.replace(url.href);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user