Port docs

integrate-vt
Marcin Kulik 8 years ago
parent c7560d30be
commit ace1d58167

@ -37,6 +37,7 @@ defmodule Asciinema.Mixfile do
{:phoenix_html, "~> 2.6"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:gettext, "~> 0.11"},
{:earmark, github: "pragdave/earmark", override: true},
{:phoenix_markdown, "~> 0.1"},
{:cowboy, "~> 1.0"}]
end

@ -3,7 +3,7 @@
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
"db_connection": {:hex, :db_connection, "1.0.0-rc.5", "1d9ab6e01387bdf2de7a16c56866971f7c2f75aea7c69cae2a0346e4b537ae0d", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0.0-beta.3", [hex: :sbroker, optional: true]}]},
"decimal": {:hex, :decimal, "1.1.2", "79a769d4657b2d537b51ef3c02d29ab7141d2b486b516c109642d453ee08e00c", [:mix], []},
"earmark": {:hex, :earmark, "1.0.1", "2c2cd903bfdc3de3f189bd9a8d4569a075b88a8981ded9a0d95672f6e2b63141", [:mix], []},
"earmark": {:git, "https://github.com/pragdave/earmark.git", "e6324c6f8d17c96ed99daa98d563860329db27b9", []},
"ecto": {:hex, :ecto, "2.0.4", "03fd3b9aa508b1383eb38c00ac389953ed22af53811aa2e504975a3e814a8d97", [:mix], [{:db_connection, "~> 1.0-rc.2", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}, {:mariaex, "~> 0.7.7", [hex: :mariaex, optional: true]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}, {:postgrex, "~> 0.11.2", [hex: :postgrex, optional: true]}, {:sbroker, "~> 1.0-beta", [hex: :sbroker, optional: true]}]},
"fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []},
"gettext": {:hex, :gettext, "0.11.0", "80c1dd42d270482418fa158ec5ba073d2980e3718bacad86f3d4ad71d5667679", [:mix], []},

@ -0,0 +1,22 @@
defmodule Asciinema.DocControllerTest do
use Asciinema.ConnCase
test "GET /docs", %{conn: conn} do
conn = get conn, "/docs"
assert redirected_to(conn, 302) == "/docs/getting-started"
end
test "GET /docs/*", %{conn: conn} do
Enum.each(["/docs/how-it-works",
"/docs/getting-started",
"/docs/installation",
"/docs/usage",
"/docs/config",
"/docs/embedding",
"/docs/faq"], fn(path) ->
conn = get conn, path
assert html_response(conn, 200) =~ "<h2>Docs</h2>"
end)
end
end

@ -1,8 +0,0 @@
defmodule Asciinema.PageControllerTest do
use Asciinema.ConnCase
test "GET /", %{conn: conn} do
conn = get conn, "/"
assert html_response(conn, 200) =~ "Welcome to Phoenix!"
end
end

@ -0,0 +1,27 @@
defmodule Asciinema.DocController do
use Asciinema.Web, :controller
alias Asciinema.{DocView, ErrorView}
@topics ["how-it-works", "getting-started", "installation", "usage", "config", "embedding", "faq"]
def index(conn, _params) do
redirect conn, to: doc_path(conn, :show, :"getting-started")
end
def show(conn, %{"topic" => topic}) when topic in @topics do
topic = String.to_atom(topic)
conn
|> assign(:topic, topic)
|> assign(:page_title, DocView.title_for(topic))
|> put_layout(:docs)
|> render("#{topic}.html")
end
def show(conn, _params) do
conn
|> put_status(404)
|> render(ErrorView, "404.html")
end
end

@ -1,7 +0,0 @@
defmodule Asciinema.PageController do
use Asciinema.Web, :controller
def index(conn, _params) do
render conn, "index.html"
end
end

@ -16,7 +16,8 @@ defmodule Asciinema.Router do
scope "/", Asciinema do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
get "/docs", DocController, :index
get "/docs/:topic", DocController, :show
end
# Other scopes may use custom stacks.

@ -0,0 +1,50 @@
# Configuration file
asciinema uses a config file to keep API token and user settings. In most cases
the location of this file is `$HOME/.config/asciinema/config`.
*NOTE! When you first run `asciinema`, local API token is generated (UUID) and
saved in the file (unless the file already exists).*
The auto-generated, minimal config file looks like this:
[api]
token = <your-api-token-here>
There are several options you can set in this file. Here's a config with all
available options set:
[api]
token = <your-api-token-here>
url = https://asciinema.example.com
[record]
command = /bin/bash -l
maxwait = 2
yes = true
quiet = true
[play]
maxwait = 1
The options in `[api]` section are related to API location and authentication.
To tell asciinema recorder to use your own asciinema site instance rather than
the default one (asciinema.org), you can set `url` option. API URL can also be
passed via `ASCIINEMA_API_URL` environment variable.
The options in `[record]` and `[play]` sections have the same meaning as the
options you pass to `asciinema rec`/`asciinema play` command (see
[Usage](/docs/usage)). If you happen to often use either `-c`, `-w` or `-y` with
these commands then consider saving it as a default in the config file.
## Configuration file locations
In fact, the following locations are checked for the presence of the config
file (in the given order):
* `$ASCIINEMA_CONFIG_HOME/config` - if you have set `$ASCIINEMA_CONFIG_HOME`
* `$XDG_CONFIG_HOME/asciinema/config` - on Linux, `$XDG_CONFIG_HOME` usually points to `$HOME/.config/`
* `$HOME/.config/asciinema/config` - in most cases it's here
* `$HOME/.asciinema/config` - created by asciinema versions prior to 1.1
The first found is used.

@ -0,0 +1,157 @@
# Sharing & embedding
You can share any recording by copying its URL and sending it to a friend or
posting it on a social network. asciinema.org supports oEmbed/Open Graph/Twitter
Card protocols, displaying a nice thumbnail where possible.
You can also easily embedded an asciicast on any HTML page. If you want to put a
recording in a blog post, project's documentation or in a conference talk
slides, you can do it by copy-pasting one of the the embed scripts.
## Sharing a link
You can get the share link for a specific asciicast by clicking on "Share" link
on asciicast page.
Any of the options listed in "Customizing the playback" section below can be
appended to the asciicast URL as the query params, e.g.:
https://asciinema.org/a/14?t=25&speed=2&theme=solarized-dark
Visiting this link will start the playback at 25s and play at double speed,
using Solarized Dark terminal theme.
## Embedding image link
Embedding as an image link is useful in places where scripts are not allowed,
e.g. in a project's README file.
You can get the embed snippets for a specific asciicast by clicking on "Share"
link on asciicast page.
This is how they look for asciicast 14:
HTML:
<a href="https://asciinema.org/a/14"><img src="https://asciinema.org/a/14.png" width="836"/></a>
Markdown:
[![asciicast](https://asciinema.org/a/14.png)](https://asciinema.org/a/14)
You can pass extra options (listed in "Customizing the playback" below) to the
linked URL as query params. For example, to start the playback automatically
when opening linked asciicast page append `?autoplay=1` to the asciicast URL in
`href` attribute:
<a href="https://asciinema.org/a/14?autoplay=1"><img src="https://asciinema.org/a/14.png" width="836"/></a>
## Embedding the player
If you're embedding on your own page or on a site which permits script tags, you
can use the full player widget.
You can get the widget script for a specific asciicast by clicking on "Embed"
link on asciicast page.
It looks like this:
<script type="text/javascript" src="https://asciinema.org/a/14.js" id="asciicast-14" async></script>
The player shows up right at the place where the script is pasted. Let's look
at the following markup:
<p>This is some text.</p>
<script type="text/javascript" src="https://asciinema.org/a/14.js" id="asciicast-14" async></script>
<p>This is some other text.</p>
The player is displayed between the two paragraphs, as a `div` element with
"asciicast" class.
The embed script supports all customization options (see the section below). An
option can be specified by adding it as a
<code>data-<em>option-name</em>="<em>value</em>"</code> attribute to the script
tag.
For example, to make the embedded player auto start playback when loaded and use
big font, use the following script:
<script type="text/javascript" src="https://asciinema.org/a/14.js" id="asciicast-14" async data-autoplay="true" data-size="big"></script>
## Customizing the playback
The player supports several options that control the behavior and look of it.
Append them to the URL (`?speed=2&theme=tango`) or set them as data attributes
on embed script (`data-speed="2" data-theme="tango"`).
### t
The `t` option specifies the time at which the playback should start. The
default is `t=0` (play from the beginning).
Accepted formats: `ss`, `mm:ss`, `hh:mm:ss`.
NOTE: when `t` is specified then `autoplay=1` is implied. To prevent the player
from starting automatically when `t` option is set you have to explicitly set
`autoplay=0`.
### autoplay
The `autoplay` option controls whether the playback should automatically start
when the player loads. Accepted values:
* 0 / false - do not start playback automatically (default)
* 1 / true - start playback automatically
### preload
The `preload` option controls whether the player should immediately start
fetching the recording.
* 0 / false - do not preload the recording, wait for user action
* 1 / true - preload the recording
Defaults to 1 for asciinema.org URLs, to 0 for embed script.
### loop
The `loop` option allows for looping the playback. This option is usually
combined with `autoplay` option. Accepted values:
* 0 / false - disable looping (default)
* 1 / true - enable looping
### speed
The `speed` option alters the playback speed. The default speed is 1 which
means it plays at the unaltered, original speed.
### size
The `size` option alters the size of the terminal font. There are 3 available
sizes:
* small (default)
* medium
* big
### theme
The `theme` option allows overriding a theme used for the terminal. It defaults
to a theme set by the asciicast author (or to "asciinema" if not set by the
author). The available themes are:
* asciinema
* tango
* solarized-dark
* solarized-light
* monokai
## oEmbed / Open Graph / Twitter Card
asciinema supports [oEmbed](http://oembed.com/), [Open Graph](http://ogp.me/)
and [Twitter Card](https://dev.twitter.com/cards/overview) APIs. When you share
an asciicast on Twitter, Slack, Facebook, Google+ or any other site which
supports one of these APIs, the asciicast is presented in a rich form (usually
with a title, author, description and a thumbnail image), linking to your
recording on asciinema.org.

@ -0,0 +1,85 @@
# Frequently Asked Questions
## How is it pronounced?
_[as-kee-nuh-muh]_.
The word “asciinema” is a combination of English “ASCII” and Ancient Greek
“κίνημα” (kínēma, “movement”).
## Why am I getting `command not found` at the begining of the recording session?
When recording asciinema starts new shell instance (as indicated by `$SHELL`
environment variable) by default. It invokes `exec $SHELL`, which in most
cases translates to `exec /bin/bash` or `exec /bin/zsh`. This means the shell
runs as an "interactive shell", but **not as a "login shell"**.
If you have functions and/or other shell configuration defined in either
`.bash_profile`, `.zprofile` or `.profile` file they are not loaded unless the
shell is started as a login shell.
Some terminal emulators do that (passing "-l" option to the shell command-line),
some don't. asciinema doesn't.
Worry not, you have several options. You can:
* move this part of configuration to `.bashrc/.zshrc`,
* record with `asciinema rec -c "/bin/bash -l"` or,
* add the following setting to your `$HOME/.config/asciinema/config` file:
[record]
command="/bin/bash -l"
## Why my shell prompt/theme isn't working during recording?
See above.
## Why some of my custom shell functions are not available during recording?
See above.
## Does it record the passwords I type during recording?
asciinema records only terminal output - everything that you can actually see
in a terminal window. It doesn't record input (keys pressed). Some
applications turn off "echo mode" when asking for a password, and because
the passwords are not visible they are not recorded. Some applications
display star characters instead of real characters and asciinema records
only "******". However, some applications don't have any precautions and
the actual password is visible to the user, and thus recorded by asciinema.
## Can I embed the asciicast player on my blog?
Yes, see [embedding docs](/docs/embedding).
## How can I delete my asciicast?
In order to delete your asciicast you need to associate your local API token
(which was assigned to the recorded asciicast) with the asciinema.org
account. Just run `asciinema auth` in your terminal and open the printed URL
in your browser. Once you sign in you'll see a "Delete" link on your
asciicast's page.
## Can I have my own asciinema site instance?
Yes, you can set up your own asciinema site. The source code of the app that
runs asciinema.org is available
[here](https://github.com/asciinema/asciinema.org).
When you have the site up and running you can easily tell asciinema client to
use it by adding following setting to _~/.config/asciinema/config_ file:
[api]
url = http://asciinema.example.com
Alternatively, you can set `ASCIINEMA_API_URL` env variable:
ASCIINEMA_API_URL=http://asciinema.example.com asciinema rec
## Can I edit/post-process the recorded asciicast?
Yes, if you know how to deal with [ansi escape
sequences](https://en.wikipedia.org/wiki/ANSI_escape_code). Asciicasts are
self-contained JSON files which you can edit before uploading. Look at
`stdout` attribute in [asciicast format doc](https://github.com/asciinema/asciinema/blob/master/doc/asciicast-v1.md)
for inspiration.

@ -0,0 +1,39 @@
<h1>Getting started</h1>
<h2>1. Install the recorder</h2>
<%= render "quick_install.html", conn: @conn %>
<h2>2. Record</h2>
<p>
To start recording run the following command:
</p>
<pre><code>asciinema rec</code></pre>
<p>
This spawns a new shell instance and records all terminal output.
When you're ready to finish simply exit the shell either by typing <code>exit</code> or
hitting <kbd>Ctrl-D</kdb>.
</p>
<p>
See <a href="<%= doc_path(@conn, :show, :usage) %>">usage instructions</a> to learn about all commands and options.
</p>
<h2>3. Manage your recordings (optional)</h2>
<p>
If you want to manage your recordings on asciinema.org (set title/description,
delete etc) you need to authenticate. Run the following command and open
displayed URL in your web browser:
</p>
<pre><code>asciinema auth</code></pre>
<p>
If you skip this step now, you can run the above command later and all
previously recorded asciicasts will automatically get assigned to your
profile.
</p>

@ -0,0 +1,74 @@
# How it works
asciinema project is built of several complementary pieces:
* command-line based terminal session recorder, `asciinema`,
* website with an API at asciinema.org,
* javascript player
When you run `asciinema rec` in your terminal the recording starts, capturing
all output that is being printed to your terminal while you're issuing the
shell commands. When the recording finishes (by hitting <kbd>Ctrl-D</kbd> or
typing `exit`) then the captured output is uploaded to asciinema.org website
and prepared for playback on the web.
Here's a brief overview of how these parts work.
## Recording
You probably know `ssh`, `screen` or `script` command. Actually, asciinema
was inspired by `script` (and `scriptreplay`) commands. What you may not know
is they all use the same UNIX system capability: [a
pseudo-terminal](http://en.wikipedia.org/wiki/Pseudo_terminal).
> A pseudo terminal is a pair of pseudo-devices, one of which, the slave,
> emulates a real text terminal device, the other of which, the master,
> provides the means by which a terminal emulator process controls the slave.
Here's how terminal emulator interfaces with a user and a shell:
> The role of the terminal emulator process is to interact with the user; to
> feed text input to the master pseudo-device for use by the shell (which is
> connected to the slave pseudo-device) and to read text output from the
> master pseudo-device and show it to the user.
In other words, pseudo-terminals give programs the ability to act as a
middlemen between the user, the display and the shell. It allows for
transparent capture of user input (keyboard) and terminal output (display).
`screen` command utilizes it for capturing special keyboard shortcuts
like <kbd>Ctrl-A</kbd> and altering the output in order to display window
numbers/names and other messages.
asciinema recorder does its job by utilizing pseudo-terminal for capturing all
the output that goes to a terminal and saving it in memory (together with timing
information). The captured output includes all the text and invisible
escape/control sequences in a raw, unaltered form. When the recording session
finishes it uploads the output (in
[asciicast format](https://github.com/asciinema/asciinema/blob/master/doc/asciicast-v1.md))
to asciinema.org. That's all about "recording" part.
For the implementation details check out [recorder source
code](https://github.com/asciinema/asciinema).
## Playback
As the recording is a raw stream of text and control
sequences it can't be just played by incrementally printing text in proper
intervals. It requires interpretation of [ANSI escape code
sequences](http://en.wikipedia.org/wiki/ANSI_escape_code) in order to
correctly display color changes, cursor movement and printing text at proper
places on the screen.
The player comes with its own terminal emulator based on
[Paul Williams' parser for ANSI-compatible video terminals](http://vt100.net/emu/dec_ansi_parser).
It covers only the display part of the emulation as this is what the player is
about (input is handled by your terminal+shell at the time of recording anyway)
and its handling of escape sequences is fully compatible with most modern
terminal emulators like xterm, Gnome Terminal, iTerm, mosh etc.
The end result is a smooth animation with all text attributes (bold,
underline, inverse, ...) and 256 colors perfectly rendered.
For the implementation details check out [asciinema.org website source
code](https://github.com/asciinema/asciinema.org) and [player source
code](https://github.com/asciinema/asciinema-player).

@ -0,0 +1,80 @@
# Installation
## Python package
asciinema is available on [PyPI](https://pypi.python.org/pypi/asciinema) and can
be installed with pip (Python 3 required):
sudo pip3 install asciinema
## Native packages
### Arch Linux
yaourt -S asciinema
### Debian
sudo apt-get install asciinema
### Fedora
For Fedora < 22:
sudo yum install asciinema
For Fedora >= 22:
sudo dnf install asciinema
### FreeBSD
Ports:
cd /usr/ports/textproc/asciinema && make install
Packages:
pkg install asciinema
### Gentoo Linux
emerge -av asciinema
### NixOS / Nix
nix-env -i go1.4-asciinema
### OS X
Homebrew:
brew update && brew install asciinema
MacPorts:
sudo port selfupdate && sudo port install asciinema
Nix:
nix-env -i go1.4-asciinema
### Ubuntu
sudo apt-add-repository ppa:zanchey/asciinema
sudo apt-get update
sudo apt-get install asciinema
### No package for your operating system?
If you use other operating system and you can build a native package
for it then don't hesitate, do it and let us know.
## Running latest version from master
If none of the above works for you (or you want to help with development) just
clone the repo and run asciinema straight from the checkout:
git clone https://github.com/asciinema/asciinema.git
cd asciinema
python3 -m asciinema --version

@ -0,0 +1,9 @@
<p>
Install asciinema with:
</p>
<pre><code>brew update && brew install asciinema</code></pre>
<p>
See other <a href="<%= doc_path(@conn, :show, :installation) %>">installation options</a>.
</p>

@ -0,0 +1 @@
<li class="<%= @class %>"><a href="<%= doc_path @conn, :show, @topic %>"><%= @text %></a></li>

@ -0,0 +1,11 @@
<h2>Docs</h2>
<ul class="nav nav-pills nav-stacked">
<%= topic_link @conn, @active_topic, :"how-it-works" %>
<%= topic_link @conn, @active_topic, :"getting-started" %>
<%= topic_link @conn, @active_topic, :installation %>
<%= topic_link @conn, @active_topic, :usage %>
<%= topic_link @conn, @active_topic, :config %>
<%= topic_link @conn, @active_topic, :embedding %>
<%= topic_link @conn, @active_topic, :faq %>
</ul>

@ -0,0 +1,110 @@
# Usage
asciinema is composed of multiple commands, similar to `git`, `apt-get` or
`brew`.
When you run `asciinema` with no arguments help message is displayed, listing
all available commands with their options.
## `rec [filename]`
__Record terminal session.__
This is the single most important command in asciinema, since it is how you
utilize this tool's main job.
By running `asciinema rec [filename]` you start a new recording session. The
command (process) that is recorded can be specified with `-c` option (see
below), and defaults to `$SHELL` which is what you want in most cases.
Recording finishes when you exit the shell (hit <kbd>Ctrl+D</kbd> or type
`exit`). If the recorded process is not a shell then recording finishes when
the process exits.
If the `filename` argument is given then the resulting recording (called
[asciicast](https://github.com/asciinema/asciinema/blob/master/doc/asciicast-v1.md))
is saved to a local file. It can later be replayed with `asciinema play
<filename>` and/or uploaded to asciinema.org with `asciinema upload
<filename>`. If the `filename` argument is omitted then (after asking for
confirmation) the resulting asciicast is uploaded to asciinema.org for further
playback in a web browser.
`ASCIINEMA_REC=1` is added to recorded process environment variables. This
can be used by your shell's config file (`.bashrc`, `.zshrc`) to alter the
prompt or play a sound when shell is being recorded.
Available options:
* `-c, --command=<command>` - Specify command to record, defaults to $SHELL
* `-t, --title=<title>` - Specify the title of the asciicast
* `-w, --max-wait=<sec>` - Reduce recorded terminal inactivity to max <sec> seconds
* `-y, --yes` - Answer "yes" to all prompts (e.g. upload confirmation)
* `-q, --quiet` - Be quiet, suppress all notices/warnings (implies -y)
## `play <filename>`
__Replay recorded asciicast in a terminal.__
This command replays given asciicast (as recorded by `rec` command) directly in
your terminal.
Playing from a local file:
asciinema play /path/to/asciicast.json
Playing from HTTP(S) URL:
asciinema play https://asciinema.org/a/22124.json
asciinema play http://example.com/demo.json
Playing from asciicast page URL (requires `<link rel="alternate"
type="application/asciicast+json" href="....json">` in page's HTML):
asciinema play https://asciinema.org/a/22124
asciinema play http://example.com/blog/post.html
Playing from stdin:
cat /path/to/asciicast.json | asciinema play -
ssh user@host cat asciicast.json | asciinema play -
Playing from IPFS:
asciinema play ipfs:/ipfs/QmcdXYJp6e4zNuimuGeWPwNMHQdxuqWmKx7NhZofQ1nw2V
asciinema play fs:/ipfs/QmcdXYJp6e4zNuimuGeWPwNMHQdxuqWmKx7NhZofQ1nw2V
Available options:
* `-w, --max-wait=<sec>` - Reduce replayed terminal inactivity to max <sec> seconds
NOTE: it is recommended to run `asciinema play` in a terminal of dimensions not
smaller than the one used for recording as there's no "transcoding" of control
sequences for new terminal size.
## `upload <filename>`
__Upload recorded asciicast to asciinema.org site.__
This command uploads given asciicast (as recorded by `rec` command) to
asciinema.org for further playback in a web browser.
`asciinema rec demo.json` + `asciinema play demo.json` + `asciinema upload
demo.json` is a nice combo for when you want to review an asciicast before
publishing it on asciinema.org.
## `auth`
__Manage recordings on asciinema.org account.__
If you want to manage your recordings on asciinema.org (set title/description,
delete etc) you need to authenticate. This command displays the URL you should
open in your web browser to do that.
On every machine you run asciinema recorder, you get a new, unique API token. If
you're already logged in on asciinema.org website and you run `asciinema auth`
from a new computer then this new device will be linked to your account.
You can synchronize your [config file](/docs/config) (which keeps
the API token) across the machines so all of them use the same token, but that's
not necessary. You can assign new tokens to your account from as many machines
as you want.

@ -0,0 +1,10 @@
<div class="container">
<div class="row">
<div class="col-md-3">
<%= render "topics.html", conn: @conn, active_topic: @topic %>
</div>
<div class="col-md-9">
<%= render @view_module, @topic_template, assigns %>
</div>
</div>
</div>

@ -0,0 +1 @@
<%= render @view_module, "wrapper.html", Map.merge(assigns, %{layout: {Asciinema.LayoutView, "app.html"}, topic_template: @view_template}) %>

@ -0,0 +1,23 @@
defmodule Asciinema.DocView do
use Asciinema.Web, :view
@titles %{
:"how-it-works" => "How it works",
:"getting-started" => "Getting started",
:installation => "Installation",
:usage => "Usage",
:config => "Configuration file",
:embedding => "Sharing & embedding",
:faq => "FAQ",
}
def title_for(topic) do
@titles[topic]
end
def topic_link(conn, current_topic, topic) do
class = if current_topic == topic, do: "active"
render "topic_link.html", conn: conn, topic: topic, text: title_for(topic), class: class
end
end
Loading…
Cancel
Save