Files
2026-07-13 13:17:40 +08:00

4270 lines
119 KiB
Python

import copy
import logging
import logging.handlers
import os
import pathlib
import random
import re
import subprocess
import urllib
import urllib.request
from collections import defaultdict
from collections.abc import Iterable
from enum import Enum
from functools import lru_cache
from queue import Queue
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
import bs4
import requests
import sphinx
import yaml
from docutils import nodes
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import PythonLexer
from sphinx.util import logging as sphinx_logging
from sphinx.util.console import red # type: ignore
from sphinx.util.nodes import make_refnode
from preprocess_github_markdown import preprocess_github_markdown_file
import json
from packaging.version import Version
logger = logging.getLogger(__name__)
__all__ = [
"DownloadAndPreprocessEcosystemDocs",
"update_context",
"LinkcheckSummarizer",
"setup_context",
"collect_example_orphans",
]
# Taken from https://github.com/edx/edx-documentation
FEEDBACK_FORM_FMT = (
"https://github.com/ray-project/ray/issues/new?"
"title={title}&labels=docs&body={body}"
)
EXAMPLE_GALLERY_CONFIGS = [
"source/data/examples.yml",
"source/serve/examples.yml",
"source/train/examples.yml",
]
# Grab a list of all files tracked by git; this is used to determine
# what pages should show an "Edit on GitHub" link (autogenerated pages
# shouldn't show this link)
TRACKED_FILES = (
subprocess.run(
["git", "ls-files", pathlib.Path(__file__).parent], capture_output=True
)
.stdout.decode("utf8")
.strip()
.split("\n")
)
_EXTERNAL_HREF_RE = re.compile(r"^[a-z][a-z0-9+.-]*:|^//", re.IGNORECASE)
def canonicalize_collection_href(
href: str, known_docs: Optional[Iterable[str]] = None
) -> str:
"""Map moved collection doc links to their generated _collections pages."""
if not known_docs or _EXTERNAL_HREF_RE.match(href) or href.startswith("#"):
return href
path, sep, fragment = href.partition("#")
if not path.endswith(".html"):
return href
docname = path[: -len(".html")]
if docname in known_docs or docname.startswith("_collections/"):
return href
collection_docname = f"_collections/{docname}"
if collection_docname in known_docs:
return f"{collection_docname}.html{sep}{fragment}"
return href
def feedback_form_url(project, page):
"""Create a URL for feedback on a particular page in a project."""
return FEEDBACK_FORM_FMT.format(
title=urllib.parse.quote("[docs] Issue on `{page}.rst`".format(page=page)),
body=urllib.parse.quote(
"# Documentation Problem/Question/Comment\n"
"<!-- Describe your issue/question/comment below. -->\n"
"<!-- If there are typos or errors in the docs, feel free "
"to create a pull-request. -->\n"
"\n\n\n\n"
"(Created directly from the docs)\n"
),
)
def update_context(app, pagename, templatename, context, doctree):
"""Update the page rendering context to include ``feedback_form_url``."""
context["feedback_form_url"] = feedback_form_url(app.config.project, pagename)
# Add doc files from external repositories to be downloaded during build here
# (repo, ref, path to get, path to save on disk)
EXTERNAL_MARKDOWN_FILES = []
class DownloadAndPreprocessEcosystemDocs:
"""
This class downloads markdown readme files for various
ecosystem libraries, saves them in specified locations and preprocesses
them before sphinx build starts.
If you have ecosystem libraries that live in a separate repo from Ray,
adding them here will allow for their docs to be present in Ray docs
without the need for duplicate files. For more details, see ``doc/README.md``.
"""
def __init__(self, base_path: str) -> None:
self.base_path = pathlib.Path(base_path).absolute()
assert self.base_path.is_dir()
self.original_docs = {}
@staticmethod
def get_latest_release_tag(repo: str) -> str:
"""repo is just the repo name, eg. ray-project/ray"""
response = requests.get(f"https://api.github.com/repos/{repo}/releases/latest")
return response.json()["tag_name"]
@staticmethod
def get_file_from_github(
repo: str, ref: str, path_to_get_from_repo: str, path_to_save_on_disk: str
) -> None:
"""If ``ref == "latest"``, use latest release"""
if ref == "latest":
ref = DownloadAndPreprocessEcosystemDocs.get_latest_release_tag(repo)
urllib.request.urlretrieve(
f"https://raw.githubusercontent.com/{repo}/{ref}/{path_to_get_from_repo}",
path_to_save_on_disk,
)
def save_original_doc(self, path: str):
with open(path, "r") as f:
self.original_docs[path] = f.read()
def write_new_docs(self, *args, **kwargs):
for (
repo,
ref,
path_to_get_from_repo,
path_to_save_on_disk,
) in EXTERNAL_MARKDOWN_FILES:
path_to_save_on_disk = self.base_path.joinpath(path_to_save_on_disk)
self.save_original_doc(path_to_save_on_disk)
self.get_file_from_github(
repo, ref, path_to_get_from_repo, path_to_save_on_disk
)
preprocess_github_markdown_file(path_to_save_on_disk)
def write_original_docs(self, *args, **kwargs):
for path, content in self.original_docs.items():
with open(path, "w") as f:
f.write(content)
def __call__(self):
self.write_new_docs()
class _BrokenLinksQueue(Queue):
"""Queue that discards messages about non-broken links."""
def __init__(self, maxsize: int = 0) -> None:
self._last_line_no = None
self.used = False
super().__init__(maxsize)
def put(self, item: logging.LogRecord, block=True, timeout=None):
self.used = True
message = item.getMessage()
# line nos are separate records
if ": line" in message:
self._last_line_no = item
# same formatting as in sphinx.builders.linkcheck
# to avoid false positives if "broken" is in url
if red("broken ") in message or "broken link:" in message:
if self._last_line_no:
super().put(self._last_line_no, block=block, timeout=timeout)
self._last_line_no = None
return super().put(item, block=block, timeout=timeout)
class _QueueHandler(logging.handlers.QueueHandler):
"""QueueHandler without modifying the record."""
def prepare(self, record: logging.LogRecord) -> logging.LogRecord:
return record
class LinkcheckSummarizer:
"""Hook into the logger used by linkcheck to display a summary at the end."""
def __init__(self) -> None:
self.logger = None
self.queue_handler = None
self.log_queue = _BrokenLinksQueue()
def add_handler_to_linkcheck(self, *args, **kwargs):
"""Adds a handler to the linkcheck logger."""
self.logger = sphinx_logging.getLogger("sphinx.builders.linkcheck")
self.queue_handler = _QueueHandler(self.log_queue)
if not self.logger.hasHandlers():
# If there are no handlers, add the one that would
# be used anyway.
self.logger.logger.addHandler(logging.lastResort)
self.logger.logger.addHandler(self.queue_handler)
def summarize(self, *args, **kwargs):
"""Summarizes broken links."""
if not self.log_queue.used:
return
self.logger.logger.removeHandler(self.queue_handler)
self.logger.info("\nBROKEN LINKS SUMMARY:\n")
has_broken_links = False
while self.log_queue.qsize() > 0:
has_broken_links = True
record: logging.LogRecord = self.log_queue.get()
self.logger.handle(record)
if not has_broken_links:
self.logger.info("No broken links found!")
def parse_navbar_config(app: sphinx.application.Sphinx, config: sphinx.config.Config):
"""Parse the navbar config file into a set of links to show in the navbar.
Parameters
----------
app : sphinx.application.Sphinx
Application instance passed when the `config-inited` event is emitted
config : sphinx.config.Config
Initialized configuration to be modified
"""
if "navbar_content_path" in config:
filename = app.config["navbar_content_path"]
else:
filename = ""
if filename:
with open(pathlib.Path(__file__).parent / filename, "r") as f:
config.navbar_content = yaml.safe_load(f)
else:
config.navbar_content = None
NavEntry = Dict[str, Union[str, List["NavEntry"]]]
def add_collapse_checkboxes(soup: bs4.BeautifulSoup) -> None:
"""Add checkboxes to collapse children in a toctree.
Vendored from ``pydata_sphinx_theme.toctree.add_collapse_checkboxes`` so the
sidebar build does not reach into a private theme API. That symbol is not
part of the theme's public interface (the module has no ``__all__``) and the
sibling ``generate_toctree_html`` that ``preload_sidebar_nav`` below is
forked from has already been removed upstream, so depending on it makes
theme upgrades fragile.
"""
# based on https://github.com/pradyunsg/furo
for element in soup.find_all("li", recursive=True):
# We check all "li" elements, to add a "current-page" to the correct li.
classes = element.get("class", [])
# expanding the parent part explicitly, if present
if "current" in classes:
parentli = element.find_parent("li", class_="toctree-l0")
if parentli:
parentli.find("details")["open"] = None
# Nothing more to do, unless this has "children"
if not element.find("ul"):
continue
# Add a class to indicate that this has children.
element["class"] = [*classes, "has-children"]
if soup.new_tag is None:
continue
# For table of contents nodes that have subtrees, we modify the HTML so
# that the subtree can be expanded or collapsed in the browser.
#
# The HTML markup tree at the parent node starts with this structure:
#
# - li.has-children
# - a.reference or p.caption
# - ul
#
# Note the first child of li.has-children is p.caption only if this node
# is a section heading. (This only happens when show_nav_level is set to
# 0.)
#
# Now we modify the tree structure in one of two ways.
#
# (1) If the node holds a section heading, the HTML tree will be
# modified like so:
#
# - li.has-children
# - details
# - summary
# - p.caption
# - .toctree-toggle
# - ul
#
# (2) Otherwise, if the node holds a link to a page in the docs:
#
# - li.has-children
# - a.reference
# - details
# - summary
# - .toctree-toggle
# - ul
#
# Why the difference? In the first case, the TOC section heading is not
# a link, but in the second case it is. So in the first case it makes
# sense to put the (non-link) text inside the summary tag so that the
# user can click either the text or the .toctree-toggle chevron icon to
# expand/collapse the TOC subtree. But in the second case, putting the
# link in the summary tag would make it unclear whether clicking on the
# link should expand the subtree or take you to the link.
# Create <details> and put the entire subtree into it
details = soup.new_tag("details")
details.extend(element.contents)
element.append(details)
# Hoist the link to the top if there is one
toc_link = element.select_one("details > a.reference")
if toc_link:
element.insert(0, toc_link)
# Create <summary> with chevron icon
summary = soup.new_tag("summary")
span = soup.new_tag(
"span",
attrs={
"class": "toctree-toggle",
# This element and the chevron it contains are purely decorative;
# the actual expand/collapse functionality is delegated to the
# <summary> tag
"role": "presentation",
},
)
span.append(soup.new_tag("i", attrs={"class": "fa-solid fa-chevron-down"}))
summary.append(span)
# Prepend section heading (if there is one) to <summary>
collapsible_section_heading = element.select_one("details > p.caption")
if collapsible_section_heading:
# Put heading inside summary so that the heading text (and chevron) are both
# clickable
summary.insert(0, collapsible_section_heading)
# Prepend <summary> to <details>
details.insert(0, summary)
# If this TOC node has a "current" class, be expanded by default
# (by opening the details/summary disclosure widget)
if "current" in classes:
details["open"] = "open"
def preload_sidebar_nav(
get_toctree: Callable[[Any], str],
pathto: Callable[[str], str],
root_doc: str,
pagename: str,
known_docs: Optional[Iterable[str]] = None,
) -> bs4.BeautifulSoup:
"""Return the navigation link structure in HTML.
This function is modified from the pydata_sphinx_theme function
`generate_toctree_html`. However, for ray we only produce one
sidebar for all pages. We therefore can call this function just once (per worker),
cache the result, and reuse it.
The result of this function is equivalent to calling
pydata_sphinx_theme.toctree.generate_toctree_html(
"sidebar",
startdepth=0,
show_nav_level=0,
collapse=False,
includehidden=True,
maxdepth=4,
titles_only=True
)
Here we cache the result on this function itself because the `get_toctree`
function is not the same instance for each `html-page-context` event, even
if the toctree content is identical.
Parameters
----------
get_toctree : Callable[[Any], str]
The function defined in context["toctree"] when html-page-context is triggered
known_docs : Optional[Iterable[str]]
Sphinx docnames known to the build environment.
Returns
-------
bs4.BeautifulSoup
Soup to display in the side navbar
"""
if hasattr(preload_sidebar_nav, "cached_toctree"):
# Need to retrieve a copy of the cached toctree HTML so as not to modify
# the cached version when we set the "checked" state of the inputs
soup = copy.copy(preload_sidebar_nav.cached_toctree)
else:
toctree = get_toctree(
collapse=False, includehidden=True, maxdepth=4, titles_only=True
)
soup = bs4.BeautifulSoup(toctree, "html.parser")
# Remove "current" class since this toctree HTML is being reused
# from some other page
for li in soup("li", {"class": "current"}):
li["class"].remove("current")
# Remove sidebar links to sub-headers on the page
for li in soup.select("li"):
# Remove
if li.find("a"):
href = li.find("a")["href"]
if "#" in href and href != "#":
li.decompose()
# Add bootstrap classes for first `ul` items
for ul in soup("ul", recursive=False):
ul.attrs["class"] = ul.attrs.get("class", []) + ["nav", "bd-sidenav"]
# Add collapse boxes for parts/captions.
# Wraps the TOC part in an extra <ul> to behave like chapters with toggles
# show_nav_level: 0 means make parts collapsible.
partcaptions = soup.find_all("p", attrs={"class": "caption"})
if len(partcaptions):
new_soup = bs4.BeautifulSoup(
"<ul class='list-caption'></ul>", "html.parser"
)
for caption in partcaptions:
# Assume that the next <ul> element is the TOC list
# for this part
for sibling in caption.next_siblings:
if sibling.name == "ul":
toclist = sibling
break
li = soup.new_tag("li", attrs={"class": "toctree-l0"})
li.extend([caption, toclist])
new_soup.ul.append(li)
soup = new_soup
# Add icons and labels for collapsible nested sections
add_collapse_checkboxes(soup)
# Cache a fresh copy of the toctree HTML
preload_sidebar_nav.cached_toctree = copy.copy(soup)
# All the URIs are relative to the current document location, e.g.
# "../../<whatever.html>". There's no need for this, and it messes up the build if
# we reuse the same sidebar (with different relative URIs for various pages) on
# different pages. Replace the leading "../" sequences in the hrefs with the correct
# number of "../" for the given rootdoc
if pagename == root_doc:
to_root_prefix = "./"
else:
to_root_prefix = re.sub(f"{root_doc}.html", "", pathto(root_doc))
for a in soup.select("a"):
href = a["href"]
if _EXTERNAL_HREF_RE.match(href):
continue
absolute_href = re.sub(r"^(\.\.\/)*", "", href)
absolute_href = canonicalize_collection_href(absolute_href, known_docs)
a["href"] = to_root_prefix + absolute_href
if absolute_href == f"{pagename}.html":
# Add a current-page class to the parent li element for styling
parent_li = a.find_parent("li")
parent_li["class"] = parent_li.get("class", []) + ["current-page"]
# Open the dropdowns of every parent li for the active page.
# pydata-sphinx-theme 0.17 replaced the <input class="toctree-checkbox">
# toggle with HTML5 <details>; keep <input> as a fallback for 0.14.
for parent_li in a.find_parents("li", attrs={"class": "has-children"}):
details = parent_li.find("details", recursive=False)
if details is not None:
details.attrs["open"] = ""
continue
el = parent_li.find("input")
if el:
el.attrs["checked"] = True
return soup
class ExampleEnum(Enum):
"""Enum which allows easier enumeration of members for example metadata."""
@classmethod
def items(cls: type) -> Iterable[Tuple["ExampleEnum", str]]:
"""Return an iterable mapping between the enum type and the corresponding value.
Returns
-------
Dict['ExampleEnum', str]
Dictionary of enum type, enum value for the enum class
"""
yield from {entry: entry.value for entry in cls}.items()
@classmethod
def values(cls: type) -> List[str]:
"""Return a list of the values of the enum.
Returns
-------
List[str]
List of values for the enum class
"""
return [entry.value for entry in cls]
@classmethod
def _missing_(cls: type, value: str) -> "ExampleEnum":
"""Allow case-insensitive lookup of enum values.
This shouldn't be called directly. Instead this is called when e.g.,
"SkillLevel('beginner')" is called.
Parameters
----------
value : str
Value for which the associated enum class instance is to be returned.
Spaces are stripped from the beginning and end of the string, and
matching is case-insensitive.
Returns
-------
ExampleEnum
Enum class instance for the requested value
"""
value = value.lstrip().rstrip().lower()
for member in cls:
if member.value.lstrip().rstrip().lower() == value:
return member
return None
@classmethod
def formatted_name(cls: type) -> str:
"""Return the formatted name for the class."""
raise NotImplementedError
@classmethod
def key(cls: type) -> str:
raise NotImplementedError
class Contributor(ExampleEnum):
RAY_TEAM = "Maintained by the Ray Team"
COMMUNITY = "Contributed by the Ray Community"
@property
def tag(self):
if self == Contributor.RAY_TEAM:
return "ray-team"
return "community"
@classmethod
def formatted_name(cls):
return "All Examples"
@classmethod
def key(cls: type) -> str:
return "contributor"
class UseCase(ExampleEnum):
"""Use case type for example metadata."""
LARGE_LANGUAGE_MODELS = "Large Language Models"
GENERATIVE_AI = "Generative AI"
COMPUTER_VISION = "Computer Vision"
NATURAL_LANGUAGE_PROCESSING = "Natural Language Processing"
TIME_SERIES_FORECASTING = "Time Series Forecasting"
RECOMMENDATION_SYSTEMS = "Recommendation Systems"
ETL = "ETL"
DATA_INGESTION = "Data Ingestion"
DATA_WAREHOUSING = "Data Warehousing"
DOCUMENT_PROCESSING = "Document Processing"
REINFORCEMENT_LEARNING = "Reinforcement Learning"
@classmethod
def formatted_name(cls):
return "Use Case"
@classmethod
def key(cls: type) -> str:
return "use_case"
class SkillLevel(ExampleEnum):
"""Skill level type for example metadata."""
BEGINNER = "Beginner"
INTERMEDIATE = "Intermediate"
ADVANCED = "Advanced"
@classmethod
def formatted_name(cls):
return "Skill Level"
@classmethod
def key(cls: type) -> str:
return "skill_level"
class Framework(ExampleEnum):
"""Framework type for example metadata."""
AWSNEURON = "AWS Neuron"
PYTORCH = "PyTorch"
JAX = "JAX"
FLAX = "Flax"
LIGHTNING = "Lightning"
TRANSFORMERS = "Transformers"
ACCELERATE = "Accelerate"
DEEPSPEED = "DeepSpeed"
TENSORFLOW = "TensorFlow"
HOROVOD = "Horovod"
XGBOOST = "XGBoost"
HUGGINGFACE = "Hugging Face"
DATAJUICER = "Data-Juicer"
VLLM = "vLLM"
PANDAS = "Pandas"
TRL = "TRL"
ANY = "Any"
UNSTRUCTURED = "Unstructured"
@classmethod
def formatted_name(cls):
return "Framework"
@classmethod
def key(cls: type) -> str:
return "framework"
class RelatedTechnology(ExampleEnum):
ML_APPLICATIONS = "ML Applications"
LLM_APPLICATIONS = "LLM Applications"
INTEGRATIONS = "Integrations"
AI_ACCELERATORS = "AI Accelerators"
DEPLOYMENT_PATTERNS = "Deployment Patterns"
@classmethod
def formatted_name(cls):
return "Related Technology"
@classmethod
def key(cls: type) -> str:
return "related_technology"
class Library(ExampleEnum):
"""Library type for example metadata."""
DATA = "Data"
SERVE = "Serve"
TRAIN = "Train"
@classmethod
def formatted_name(cls):
return "Library"
@classmethod
def key(cls: type) -> str:
return "library"
@classmethod
def from_path(cls, path: Union[pathlib.Path, str]) -> "Library":
"""Instantiate a Library instance from a path.
This function works its way up the file tree until it finds a directory that
matches one of the enum types; if none is found, raise an exception.
Parameters
----------
path : Union[pathlib.Path, str]
Path containing a directory named the same as one of the enum types.
Returns
-------
Library
A new Library instance for the given path.
"""
if isinstance(path, str):
path = pathlib.Path(path)
for part in path.parts:
try:
return Library(part)
except ValueError:
continue
raise ValueError(f"Cannot find library name from example config path {path}")
class Example:
"""Class containing metadata about an example to be shown in the example gallery."""
def __init__(
self, config: Dict[str, str], library: Library, config_dir: pathlib.Path
):
self.config_dir = config_dir
self.library = library
self.frameworks = []
for framework in config.get("frameworks", []):
self.frameworks.append(Framework(framework.strip()))
self.use_cases = []
for use_case in config.get("use_cases", []):
self.use_cases.append(UseCase(use_case.strip()))
contributor = config.get("contributor", "").strip()
if contributor:
if contributor == "community":
self.contributor = Contributor.COMMUNITY
elif contributor == "ray team":
self.contributor = Contributor.RAY_TEAM
else:
raise ValueError(
f"Invalid contributor type specified: {contributor}. Must be "
" either 'ray team' or 'community'."
)
else:
self.contributor = Contributor.RAY_TEAM
related_technology = config.get("related_technology", "").strip()
if related_technology:
self.related_technology = RelatedTechnology(related_technology)
self.skill_level = SkillLevel(config.get("skill_level"))
self.title = config.get("title")
if "link" not in config:
raise ValueError(
"All examples must provide a link to either an external resource "
"(starting with http://...) or a relative path to an internal page "
"that is part of the Ray docs."
)
link = config["link"]
if link.startswith("http"):
self.link = link
else:
self.link = str(config_dir / link)
if self.title is None:
raise ValueError(f"Title of an example {config} must be set.")
if self.link is None:
raise ValueError(
f"An internal or external link must be specified: {config}"
)
class ExampleConfig:
"""Object which holds configuration data for a set of library examples."""
def __init__(
self, path: Union[pathlib.Path, str], src_dir: Union[pathlib.Path, str]
):
"""Parse a config file containing examples to display in the example gallery.
Parameters
----------
path : Union[pathlib.Path, str]
Path to the `examples.yml` to be parsed
root_dir : Union[pathlib.Path, str]
Path to the root of the docs directory; `app.srcdir`
"""
if isinstance(path, str):
path = pathlib.Path(path)
if not path.exists():
raise ValueError(f"No configuration file found at {path}.")
with open(path, "r") as f:
config = yaml.safe_load(f)
self.config_path = path
self.library = Library.from_path(path)
self.examples = self.parse_examples(config.get("examples", []), src_dir)
self.text = config.get("text", "")
self.columns_to_show = self.parse_columns_to_show(
config.get("columns_to_show", [])
)
groupby = config.get("groupby", "skill_level")
for cls in ExampleEnum.__subclasses__():
if cls.key() == groupby:
self.groupby = cls
break
else:
valid_classes = [cls.key() for cls in ExampleEnum.__subclasses__()]
raise ValueError(
f"Unable to find class to group example entries by {groupby}. "
f"Valid choices are {valid_classes}.",
)
def parse_columns_to_show(self, columns: str) -> Dict[str, type]:
"""Parse the columns to show in the library example page for the config.
Note that a link to the example is always shown, and cannot be hidden.
Parameters
----------
columns : str
Column names to show; valid names are any subclass of ExampleEnum, e.g.
UseCase, Framework, etc.
Returns
-------
List[type]
A list of the ExampleEnum classes for which columns are to be shown
"""
cols = {}
for col in columns:
if col == "use_cases":
cols["use_cases"] = UseCase
elif col == "frameworks":
cols["frameworks"] = Framework
else:
raise ValueError(
f"Invalid column name {col} specified in {self.config_path}"
)
return cols
def parse_examples(
self, example_config: List[Dict[str, str]], src_dir: Union[pathlib.Path, str]
) -> List[Example]:
"""Parse the examples in the given configuration.
Raise an exception if duplicate examples are found in the configuration file.
Parameters
----------
path : pathlib.Path
Path to the example file to parse
Returns
-------
List[Example]
List of examples from the parsed configuration file
"""
links = set()
examples = []
for entry in example_config:
example = Example(
entry, self.library, self.config_path.relative_to(src_dir).parent
)
if example.link in links:
raise ValueError(
f"A duplicate example {example.link} was specified in "
f"{self.config_path}. Please remove duplicates and rebuild."
)
links.add(example.link)
examples.append(example)
return examples
def __iter__(self):
yield from self.examples
def setup_context(app, pagename, templatename, context, doctree):
def render_library_examples(config: pathlib.Path = None) -> bs4.BeautifulSoup:
"""Render a table of links to examples for a given Ray library.
Duplicate examples will result in an error.
Parameters
----------
config : pathlib.Path
Path to the examples.yml file for the Ray library
Returns
-------
bs4.BeautifulSoup
Table of links to examples for the library, rendered as HTML
"""
if config is None:
config = (pathlib.Path(app.confdir) / pagename).parent / "examples.yml"
# Keep track of whether any of the examples have frameworks metadata; the
# column will not be shown if no frameworks metadata exists on any example.
# Group the examples by the ExampleConfig.groupby value:
examples = defaultdict(list)
example_config = ExampleConfig(config, app.srcdir)
for example in example_config:
try:
group = getattr(example, example_config.groupby.key())
except AttributeError as e:
raise AttributeError(
f"Example {example.link} has no {example_config.groupby.key()} "
"key, but needs one because the examples for library "
f"{example_config.library.value} are configured to be grouped "
f"by {example_config.groupby.key()}."
) from e
examples[group].append(example)
# Construct a table of examples
soup = bs4.BeautifulSoup()
# Add the main heading to the page and include the page text
page_title = soup.new_tag("h1")
page_title.append(f"{example_config.library.value} Examples")
soup.append(page_title)
page_text = soup.new_tag("p")
page_text.append(example_config.text)
soup.append(page_text)
container = soup.new_tag("div", attrs={"class": "example-index"})
for group, group_examples in examples.items():
if not group_examples:
continue
header = soup.new_tag("h2", attrs={"class": "example-header"})
header.append(group.value)
container.append(header)
table = soup.new_tag("table", attrs={"class": ["table", "example-table"]})
# If there are additional columns to show besides just the example link,
# include column titles in the table header
if len(example_config.columns_to_show) > 0:
thead = soup.new_tag("thead")
thead_row = soup.new_tag("tr")
for example_enum in example_config.columns_to_show.values():
col_header = soup.new_tag("th")
col_label = soup.new_tag("p")
col_label.append(example_enum.formatted_name())
col_header.append(col_label)
thead_row.append(col_header)
link_col = soup.new_tag("th")
link_label = soup.new_tag("p")
link_label.append("Example")
link_col.append(link_label)
thead_row.append(link_col)
thead.append(thead_row)
table.append(thead)
tbody = soup.new_tag("tbody")
for example in group_examples:
tr = soup.new_tag("tr")
# The columns specify which attributes of each example to show;
# for each attribute, a new cell value is added with the attribute from
# the example
if len(example_config.columns_to_show) > 0:
for attribute in example_config.columns_to_show:
col_td = soup.new_tag("td")
col_p = soup.new_tag("p")
attribute_value = getattr(example, attribute, "")
if isinstance(attribute_value, str):
col_p.append(attribute_value)
elif isinstance(attribute_value, list):
col_p.append(
", ".join(item.value for item in attribute_value)
)
col_td.append(col_p)
tr.append(col_td)
link_td = soup.new_tag("td")
link_p = soup.new_tag("p")
if example.link.startswith("http"):
link_href = soup.new_tag("a", attrs={"href": example.link})
else:
link_href = soup.new_tag(
"a", attrs={"href": context["pathto"](example.link)}
)
link_span = soup.new_tag("span")
link_span.append(example.title)
link_href.append(link_span)
link_p.append(link_href)
link_td.append(link_p)
tr.append(link_td)
tbody.append(tr)
table.append(tbody)
container.append(table)
soup.append(container)
return soup
def render_example_gallery(configs: Iterable[pathlib.Path] = None) -> str:
"""Load and render examples for the example gallery.
This function grabs examples from the various example gallery indexes.
Each Ray library team maintains its own yml file which a standardized
set of metadata with each example to ensure consistency across the
code base.
Duplicate examples will not raise an error as long as their fields match
exactly.
Parameters
----------
configs : Iterable[pathlib.Path]
Paths to example gallery files to ingest
Returns
-------
str
Example gallery examples rendered as HTML
"""
if configs is None:
configs = EXAMPLE_GALLERY_CONFIGS
examples = {}
for config in configs:
config_path = pathlib.Path(config).relative_to("source")
example_config = ExampleConfig(
pathlib.Path(app.confdir) / config_path, app.srcdir
)
for example in example_config:
# Check the ingested examples for duplicates. If there are duplicates,
# check that they have the same field values, and keep only one.
if example.link in examples:
existing_example_fields = vars(examples[example.link])
for key, value in vars(example):
if existing_example_fields.get(key) != value:
raise ValueError(
"One example was specified twice with different "
f"attributes: {vars(example)}\n"
f"{existing_example_fields}"
)
else:
examples[example.link] = example
soup = bs4.BeautifulSoup()
list_area = soup.new_tag("div", attrs={"class": ["example-list-area"]})
for example in examples.values():
example_div = soup.new_tag("div", attrs={"class": "example"})
if example.link.startswith("http"):
link = example.link
else:
link = context["pathto"](example.link)
example_link = soup.new_tag(
"a",
attrs={
"class": "example-link",
"href": link,
},
)
example_icon_area = soup.new_tag(
"div", attrs={"class": "example-icon-area"}
)
icon = soup.new_tag(
"img",
attrs={
"class": "example-icon",
"src": f"../_static/img/icon_bg_{random.randint(1, 5)}.jpg",
},
)
remix_icon = soup.new_tag(
"i", attrs={"class": f"{random.choice(REMIX_ICONS)} remix-icon"}
)
icon.append(remix_icon)
example_icon_area.append(icon)
example_link.append(example_icon_area)
example_text_area = soup.new_tag(
"div", attrs={"class": "example-text-area"}
)
example_title = soup.new_tag("b", attrs={"class": "example-title"})
example_title.append(example.title)
example_text_area.append(example_title)
example_tags = soup.new_tag("span", attrs={"class": "example-tags"})
frameworks = [item.value for item in example.frameworks]
example_tags.append(
". ".join(
[example.skill_level.value, example.library.value, *frameworks]
)
+ "."
)
example_text_area.append(example_tags)
other_keywords = soup.new_tag(
"span", attrs={"class": "example-other-keywords"}
)
other_keywords.append(
" ".join(use_case.value for use_case in example.use_cases)
)
other_keywords.append(f" {example.contributor.tag}")
example_text_area.append(other_keywords)
# Add the appropriate text if the example comes from the community
if example.contributor == Contributor.COMMUNITY:
community_text_area = soup.new_tag(
"div", attrs={"class": "community-text-area"}
)
community_text = soup.new_tag("i", attrs={"class": "community-text"})
community_text.append("*Contributed by the Ray Community")
community_text_area.append(community_text)
# Add emojis separately; they're not italicized in the mockups
emojis = soup.new_tag("span", attrs={"class": "community-emojis"})
emojis.append("💪 ✨")
community_text_area.append(emojis)
example_text_area.append(community_text_area)
example_link.append(example_text_area)
example_div.append(example_link)
list_area.append(example_div)
soup.append(list_area)
return soup
@lru_cache(maxsize=None)
def render_header_nav_links() -> bs4.BeautifulSoup:
"""Render external header links into the top nav bar.
The structure rendered here is defined in an external yaml file.
Returns
-------
str
Raw HTML to be rendered in the top nav bar
"""
if not hasattr(app.config, "navbar_content"):
raise ValueError(
"A template is attempting to call render_header_nav_links(); a "
"navbar configuration must be specified."
)
node = nodes.container(classes=["navbar-content"])
node.append(render_header_nodes(app.config.navbar_content))
header_soup = bs4.BeautifulSoup(
app.builder.render_partial(node)["fragment"], "html.parser"
)
return add_nav_chevrons(header_soup)
def render_header_nodes(
obj: List[NavEntry], is_top_level: bool = True
) -> nodes.Node:
"""Generate a set of header nav links with docutils nodes.
Parameters
----------
is_top_level : bool
True if the call to this function is rendering the top level nodes,
False otherwise (non-top level nodes are displayed as submenus of the top
level nodes)
obj : List[NavEntry]
List of yaml config entries to render as docutils nodes
Returns
-------
nodes.Node
Bullet list which will be turned into header nav HTML by the sphinx builder
"""
bullet_list = nodes.bullet_list(
bullet="-",
classes=["navbar-toplevel" if is_top_level else "navbar-sublevel"],
)
for item in obj:
if "file" in item:
ref_node = make_refnode(
app.builder,
context["current_page_name"],
item["file"],
None,
nodes.inline(classes=["navbar-link-title"], text=item.get("title")),
item.get("title"),
)
elif "link" in item:
ref_node = nodes.reference("", "", internal=False)
ref_node["refuri"] = item.get("link")
ref_node["reftitle"] = item.get("title")
ref_node.append(
nodes.inline(classes=["navbar-link-title"], text=item.get("title"))
)
if "caption" in item:
caption = nodes.Text(item.get("caption"))
ref_node.append(caption)
paragraph = nodes.paragraph()
paragraph.append(ref_node)
container = nodes.container(classes=["ref-container"])
container.append(paragraph)
list_item = nodes.list_item(
classes=["active-link"] if item.get("file") == pagename else []
)
list_item.append(container)
if "sections" in item:
wrapper = nodes.container(classes=["navbar-dropdown"])
wrapper.append(
render_header_nodes(item["sections"], is_top_level=False)
)
list_item.append(wrapper)
bullet_list.append(list_item)
return bullet_list
def render_use_cases_dropdown() -> bs4.BeautifulSoup:
return render_example_gallery_dropdown(UseCase)
def render_libraries_dropdown() -> bs4.BeautifulSoup:
return render_example_gallery_dropdown(Library)
def render_frameworks_dropdown() -> bs4.BeautifulSoup:
return render_example_gallery_dropdown(Framework)
def render_contributor_dropdown() -> bs4.BeautifulSoup:
return render_example_gallery_dropdown(Contributor)
def show_edit_button() -> bool:
"""Check whether to show the "Edit on GitHub" button on the current page.
Returns
-------
bool
True if the page is tracked by git, and therefore should show
an "Edit on GitHub" button; False otherwise (which happens e.g.
when the page is generated during the docs build process)
"""
if "page_source_suffix" in context:
file_name = f"{pagename}{context['page_source_suffix']}"
return file_name in TRACKED_FILES
return False
context["show_edit_button"] = show_edit_button()
context["cached_toctree"] = preload_sidebar_nav(
context["toctree"],
context["pathto"],
context["root_doc"],
pagename,
getattr(app.env, "found_docs", None),
)
context["render_header_nav_links"] = render_header_nav_links
context["render_library_examples"] = render_library_examples
context["render_example_gallery"] = render_example_gallery
context["render_use_cases_dropdown"] = render_use_cases_dropdown
context["render_libraries_dropdown"] = render_libraries_dropdown
context["render_frameworks_dropdown"] = render_frameworks_dropdown
context["render_contributor_dropdown"] = render_contributor_dropdown
# Update the HTML page context with a few extra utilities.
context["pygments_highlight_python"] = lambda code: highlight(
code, PythonLexer(), HtmlFormatter()
)
def update_hrefs(input_soup: bs4.BeautifulSoup, n_levels_deep=0):
soup = copy.copy(input_soup)
for a in soup.select("a"):
a["href"] = "../" * n_levels_deep + a["href"]
return soup
def add_nav_chevrons(input_soup: bs4.BeautifulSoup) -> bs4.BeautifulSoup:
"""Add dropdown chevron icons to the header nav bar.
Parameters
----------
input_soup : bs4.BeautifulSoup
Soup containing rendered HTML which will be inserted into the header nav bar
Returns
-------
bs4.BeautifulSoup
A new BeautifulSoup instance containing chevrons on the list items that
are meant to be dropdowns.
"""
soup = copy.copy(input_soup)
for li in soup.find_all("li", recursive=True):
divs = li.find_all("div", {"class": "navbar-dropdown"}, recursive=False)
if divs:
ref = li.find("div", {"class": "ref-container"})
ref.append(soup.new_tag("i", attrs={"class": "fa-solid fa-chevron-down"}))
return soup
def render_example_gallery_dropdown(cls: type) -> bs4.BeautifulSoup:
"""Render a dropdown menu selector for the example gallery.
Parameters
----------
cls : type
ExampleEnum class type to use to populate the dropdown
Returns
-------
bs4.BeautifulSoup
Soup containing the dropdown element
"""
soup = bs4.BeautifulSoup()
dropdown_name = cls.formatted_name().lower().replace(" ", "-")
dropdown_container = soup.new_tag(
"div", attrs={"class": "filter-dropdown", "id": f"{dropdown_name}-dropdown"}
)
dropdown_show_checkbox = soup.new_tag(
"input",
attrs={
"class": "dropdown-checkbox",
"id": f"{dropdown_name}-checkbox",
"type": "checkbox",
},
)
dropdown_container.append(dropdown_show_checkbox)
dropdown_label = soup.new_tag(
"label", attrs={"class": "dropdown-label", "for": f"{dropdown_name}-checkbox"}
)
dropdown_label.append(cls.formatted_name())
chevron = soup.new_tag("i", attrs={"class": "fa-solid fa-chevron-down"})
dropdown_label.append(chevron)
dropdown_container.append(dropdown_label)
if cls.values():
dropdown_options = soup.new_tag("div", attrs={"class": "dropdown-content"})
for member in list(cls):
label = soup.new_tag("label", attrs={"class": "checkbox-container"})
label.append(member.value)
tag = getattr(member, "tag", member.value)
checkbox = soup.new_tag(
"input",
attrs={
"id": f"{tag}-checkbox",
"class": "filter-checkbox",
"type": "checkbox",
},
)
label.append(checkbox)
checkmark = soup.new_tag("span", attrs={"class": "checkmark"})
label.append(checkmark)
dropdown_options.append(label)
dropdown_container.append(dropdown_options)
soup.append(dropdown_container)
return soup
def collect_example_orphans(
confdir: os.PathLike,
srcdir: os.PathLike,
example_configs: Optional[List[str]] = None,
) -> set:
"""Collect document paths from examples.yml files to mark as orphans.
This function parses example configuration files and returns a set of
document paths that should be marked as orphan documents during the build.
Parameters
----------
confdir : os.PathLike
Path to the Sphinx configuration directory (app.confdir)
srcdir : os.PathLike
Path to the Sphinx source directory (app.srcdir)
example_configs : Optional[List[str]]
Configuration files to parse. Defaults to EXAMPLE_GALLERY_CONFIGS.
Returns
-------
set
Set of document paths (without file extensions) to mark as orphans
"""
if example_configs is None:
example_configs = EXAMPLE_GALLERY_CONFIGS
example_orphan_documents = set()
for config in example_configs:
config_path = pathlib.Path(confdir) / pathlib.Path(config).relative_to(
"source"
)
example_config = ExampleConfig(config_path, srcdir)
for example in example_config:
if not example.link.startswith("http"):
# Normalize path and remove file extension to get docname
normalized = pathlib.PurePath(os.path.normpath(example.link))
docname = str(normalized.with_suffix(''))
example_orphan_documents.add(docname)
return example_orphan_documents
def pregenerate_example_rsts(
app: sphinx.application.Sphinx, *example_configs: Optional[List[str]]
):
"""Pregenerate RST files for the example page configuration files.
This generates RST files for displaying example pages for Ray libraries.
See `add_custom_assets` for more information about the custom template
that gets rendered from these configuration files.
Parameters
----------
*example_configs : Optional[List[str]]
Configuration files for which example pages are to be generated
"""
if not example_configs:
example_configs = EXAMPLE_GALLERY_CONFIGS
for config in example_configs:
# Depending on where the sphinx build command is run from, the path to the
# target config file can change. Handle these paths manually to ensure it
# works on RTD and locally.
config_path = pathlib.Path(app.confdir) / pathlib.Path(config).relative_to(
"source"
)
page_title = "Examples"
title_decoration = "=" * len(page_title)
with open(config_path.with_suffix(".rst"), "w") as f:
f.write(
f"{page_title}\n{title_decoration}\n\n"
" .. this file is pregenerated; please edit ./examples.yml to "
"modify examples for this library."
)
def generate_version_url(version):
return f"https://docs.ray.io/en/{version}/"
def generate_versions_json():
"""Gets the releases from the remote repo, sorts them in semver order,
and generates the JSON needed for the version switcher
"""
ray_prefix = "ray-"
min_version = "1.11.0"
repo_url = "https://github.com/ray-project/ray.git"
static_dir_name = "_static"
version_json_filename = "versions.json"
dereference_suffix = "^{}"
version_json_data = []
# Versions that should always appear at the top
for version in ["latest", "master"]:
version_json_data.append(
{"version": version, "url": generate_version_url(version)}
)
git_versions = []
# Fetch release tags from repo
output = subprocess.check_output(["git", "ls-remote", "--tags", repo_url]).decode(
"utf-8"
)
# Extract release versions from tags
tags = re.findall(r"refs/tags/(.+)", output)
for tag in tags:
if ray_prefix in tag and dereference_suffix not in tag:
version = tag.split(ray_prefix)[1]
if version not in git_versions and Version(version) >= Version(min_version):
git_versions.append(version)
git_versions.sort(key=Version, reverse=True)
for version in git_versions:
version_json_data.append(
{
"version": f"releases-{version}",
"url": generate_version_url(f"releases-{version}"),
}
)
# Ensure static path exists
static_dir = os.path.join(os.path.dirname(__file__), static_dir_name)
if not os.path.exists(static_dir):
os.makedirs(static_dir)
# Write JSON output
output_path = os.path.join(static_dir, version_json_filename)
with open(output_path, "w") as f:
json.dump(version_json_data, f, indent=4)
REMIX_ICONS = [
"ri-24-hours-fill",
"ri-24-hours-line",
"ri-4k-fill",
"ri-4k-line",
"ri-a-b",
"ri-account-box-fill",
"ri-account-box-line",
"ri-account-circle-fill",
"ri-account-circle-line",
"ri-account-pin-box-fill",
"ri-account-pin-box-line",
"ri-account-pin-circle-fill",
"ri-account-pin-circle-line",
"ri-add-box-fill",
"ri-add-box-line",
"ri-add-circle-fill",
"ri-add-circle-line",
"ri-add-fill",
"ri-add-line",
"ri-admin-fill",
"ri-admin-line",
"ri-advertisement-fill",
"ri-advertisement-line",
"ri-airplay-fill",
"ri-airplay-line",
"ri-alarm-fill",
"ri-alarm-line",
"ri-alarm-warning-fill",
"ri-alarm-warning-line",
"ri-album-fill",
"ri-album-line",
"ri-alert-fill",
"ri-alert-line",
"ri-aliens-fill",
"ri-aliens-line",
"ri-align-bottom",
"ri-align-center",
"ri-align-justify",
"ri-align-left",
"ri-align-right",
"ri-align-top",
"ri-align-vertically",
"ri-alipay-fill",
"ri-alipay-line",
"ri-amazon-fill",
"ri-amazon-line",
"ri-anchor-fill",
"ri-anchor-line",
"ri-ancient-gate-fill",
"ri-ancient-gate-line",
"ri-ancient-pavilion-fill",
"ri-ancient-pavilion-line",
"ri-android-fill",
"ri-android-line",
"ri-angularjs-fill",
"ri-angularjs-line",
"ri-anticlockwise-2-fill",
"ri-anticlockwise-2-line",
"ri-anticlockwise-fill",
"ri-anticlockwise-line",
"ri-app-store-fill",
"ri-app-store-line",
"ri-apple-fill",
"ri-apple-line",
"ri-apps-2-fill",
"ri-apps-2-line",
"ri-apps-fill",
"ri-apps-line",
"ri-archive-drawer-fill",
"ri-archive-drawer-line",
"ri-archive-fill",
"ri-archive-line",
"ri-arrow-down-circle-fill",
"ri-arrow-down-circle-line",
"ri-arrow-down-fill",
"ri-arrow-down-line",
"ri-arrow-down-s-fill",
"ri-arrow-down-s-line",
"ri-arrow-drop-down-fill",
"ri-arrow-drop-down-line",
"ri-arrow-drop-left-fill",
"ri-arrow-drop-left-line",
"ri-arrow-drop-right-fill",
"ri-arrow-drop-right-line",
"ri-arrow-drop-up-fill",
"ri-arrow-drop-up-line",
"ri-arrow-go-back-fill",
"ri-arrow-go-back-line",
"ri-arrow-go-forward-fill",
"ri-arrow-go-forward-line",
"ri-arrow-left-circle-fill",
"ri-arrow-left-circle-line",
"ri-arrow-left-down-fill",
"ri-arrow-left-down-line",
"ri-arrow-left-fill",
"ri-arrow-left-line",
"ri-arrow-left-right-fill",
"ri-arrow-left-right-line",
"ri-arrow-left-s-fill",
"ri-arrow-left-s-line",
"ri-arrow-left-up-fill",
"ri-arrow-left-up-line",
"ri-arrow-right-circle-fill",
"ri-arrow-right-circle-line",
"ri-arrow-right-down-fill",
"ri-arrow-right-down-line",
"ri-arrow-right-fill",
"ri-arrow-right-line",
"ri-arrow-right-s-fill",
"ri-arrow-right-s-line",
"ri-arrow-right-up-fill",
"ri-arrow-right-up-line",
"ri-arrow-up-circle-fill",
"ri-arrow-up-circle-line",
"ri-arrow-up-down-fill",
"ri-arrow-up-down-line",
"ri-arrow-up-fill",
"ri-arrow-up-line",
"ri-arrow-up-s-fill",
"ri-arrow-up-s-line",
"ri-artboard-2-fill",
"ri-artboard-2-line",
"ri-artboard-fill",
"ri-artboard-line",
"ri-article-fill",
"ri-article-line",
"ri-aspect-ratio-fill",
"ri-aspect-ratio-line",
"ri-asterisk",
"ri-at-fill",
"ri-at-line",
"ri-attachment-2",
"ri-attachment-fill",
"ri-attachment-line",
"ri-auction-fill",
"ri-auction-line",
"ri-award-fill",
"ri-award-line",
"ri-baidu-fill",
"ri-baidu-line",
"ri-ball-pen-fill",
"ri-ball-pen-line",
"ri-bank-card-2-fill",
"ri-bank-card-2-line",
"ri-bank-card-fill",
"ri-bank-card-line",
"ri-bank-fill",
"ri-bank-line",
"ri-bar-chart-2-fill",
"ri-bar-chart-2-line",
"ri-bar-chart-box-fill",
"ri-bar-chart-box-line",
"ri-bar-chart-fill",
"ri-bar-chart-grouped-fill",
"ri-bar-chart-grouped-line",
"ri-bar-chart-horizontal-fill",
"ri-bar-chart-horizontal-line",
"ri-bar-chart-line",
"ri-barcode-box-fill",
"ri-barcode-box-line",
"ri-barcode-fill",
"ri-barcode-line",
"ri-barricade-fill",
"ri-barricade-line",
"ri-base-station-fill",
"ri-base-station-line",
"ri-basketball-fill",
"ri-basketball-line",
"ri-battery-2-charge-fill",
"ri-battery-2-charge-line",
"ri-battery-2-fill",
"ri-battery-2-line",
"ri-battery-charge-fill",
"ri-battery-charge-line",
"ri-battery-fill",
"ri-battery-line",
"ri-battery-low-fill",
"ri-battery-low-line",
"ri-battery-saver-fill",
"ri-battery-saver-line",
"ri-battery-share-fill",
"ri-battery-share-line",
"ri-bear-smile-fill",
"ri-bear-smile-line",
"ri-behance-fill",
"ri-behance-line",
"ri-bell-fill",
"ri-bell-line",
"ri-bike-fill",
"ri-bike-line",
"ri-bilibili-fill",
"ri-bilibili-line",
"ri-bill-fill",
"ri-bill-line",
"ri-billiards-fill",
"ri-billiards-line",
"ri-bit-coin-fill",
"ri-bit-coin-line",
"ri-blaze-fill",
"ri-blaze-line",
"ri-bluetooth-connect-fill",
"ri-bluetooth-connect-line",
"ri-bluetooth-fill",
"ri-bluetooth-line",
"ri-blur-off-fill",
"ri-blur-off-line",
"ri-body-scan-fill",
"ri-body-scan-line",
"ri-bold",
"ri-book-2-fill",
"ri-book-2-line",
"ri-book-3-fill",
"ri-book-3-line",
"ri-book-fill",
"ri-book-line",
"ri-book-marked-fill",
"ri-book-marked-line",
"ri-book-open-fill",
"ri-book-open-line",
"ri-book-read-fill",
"ri-book-read-line",
"ri-booklet-fill",
"ri-booklet-line",
"ri-bookmark-2-fill",
"ri-bookmark-2-line",
"ri-bookmark-3-fill",
"ri-bookmark-3-line",
"ri-bookmark-fill",
"ri-bookmark-line",
"ri-boxing-fill",
"ri-boxing-line",
"ri-braces-fill",
"ri-braces-line",
"ri-brackets-fill",
"ri-brackets-line",
"ri-briefcase-2-fill",
"ri-briefcase-2-line",
"ri-briefcase-3-fill",
"ri-briefcase-3-line",
"ri-briefcase-4-fill",
"ri-briefcase-4-line",
"ri-briefcase-5-fill",
"ri-briefcase-5-line",
"ri-briefcase-fill",
"ri-briefcase-line",
"ri-bring-forward",
"ri-bring-to-front",
"ri-broadcast-fill",
"ri-broadcast-line",
"ri-brush-2-fill",
"ri-brush-2-line",
"ri-brush-3-fill",
"ri-brush-3-line",
"ri-brush-4-fill",
"ri-brush-4-line",
"ri-brush-fill",
"ri-brush-line",
"ri-bubble-chart-fill",
"ri-bubble-chart-line",
"ri-bug-2-fill",
"ri-bug-2-line",
"ri-bug-fill",
"ri-bug-line",
"ri-building-2-fill",
"ri-building-2-line",
"ri-building-3-fill",
"ri-building-3-line",
"ri-building-4-fill",
"ri-building-4-line",
"ri-building-fill",
"ri-building-line",
"ri-bus-2-fill",
"ri-bus-2-line",
"ri-bus-fill",
"ri-bus-line",
"ri-bus-wifi-fill",
"ri-bus-wifi-line",
"ri-cactus-fill",
"ri-cactus-line",
"ri-cake-2-fill",
"ri-cake-2-line",
"ri-cake-3-fill",
"ri-cake-3-line",
"ri-cake-fill",
"ri-cake-line",
"ri-calculator-fill",
"ri-calculator-line",
"ri-calendar-2-fill",
"ri-calendar-2-line",
"ri-calendar-check-fill",
"ri-calendar-check-line",
"ri-calendar-event-fill",
"ri-calendar-event-line",
"ri-calendar-fill",
"ri-calendar-line",
"ri-calendar-todo-fill",
"ri-calendar-todo-line",
"ri-camera-2-fill",
"ri-camera-2-line",
"ri-camera-3-fill",
"ri-camera-3-line",
"ri-camera-fill",
"ri-camera-lens-fill",
"ri-camera-lens-line",
"ri-camera-line",
"ri-camera-off-fill",
"ri-camera-off-line",
"ri-camera-switch-fill",
"ri-camera-switch-line",
"ri-capsule-fill",
"ri-capsule-line",
"ri-car-fill",
"ri-car-line",
"ri-car-washing-fill",
"ri-car-washing-line",
"ri-caravan-fill",
"ri-caravan-line",
"ri-cast-fill",
"ri-cast-line",
"ri-cellphone-fill",
"ri-cellphone-line",
"ri-celsius-fill",
"ri-celsius-line",
"ri-centos-fill",
"ri-centos-line",
"ri-character-recognition-fill",
"ri-character-recognition-line",
"ri-charging-pile-2-fill",
"ri-charging-pile-2-line",
"ri-charging-pile-fill",
"ri-charging-pile-line",
"ri-chat-1-fill",
"ri-chat-1-line",
"ri-chat-2-fill",
"ri-chat-2-line",
"ri-chat-3-fill",
"ri-chat-3-line",
"ri-chat-4-fill",
"ri-chat-4-line",
"ri-chat-check-fill",
"ri-chat-check-line",
"ri-chat-delete-fill",
"ri-chat-delete-line",
"ri-chat-download-fill",
"ri-chat-download-line",
"ri-chat-follow-up-fill",
"ri-chat-follow-up-line",
"ri-chat-forward-fill",
"ri-chat-forward-line",
"ri-chat-heart-fill",
"ri-chat-heart-line",
"ri-chat-history-fill",
"ri-chat-history-line",
"ri-chat-new-fill",
"ri-chat-new-line",
"ri-chat-off-fill",
"ri-chat-off-line",
"ri-chat-poll-fill",
"ri-chat-poll-line",
"ri-chat-private-fill",
"ri-chat-private-line",
"ri-chat-quote-fill",
"ri-chat-quote-line",
"ri-chat-settings-fill",
"ri-chat-settings-line",
"ri-chat-smile-2-fill",
"ri-chat-smile-2-line",
"ri-chat-smile-3-fill",
"ri-chat-smile-3-line",
"ri-chat-smile-fill",
"ri-chat-smile-line",
"ri-chat-upload-fill",
"ri-chat-upload-line",
"ri-chat-voice-fill",
"ri-chat-voice-line",
"ri-check-double-fill",
"ri-check-double-line",
"ri-check-fill",
"ri-check-line",
"ri-checkbox-blank-circle-fill",
"ri-checkbox-blank-circle-line",
"ri-checkbox-blank-fill",
"ri-checkbox-blank-line",
"ri-checkbox-circle-fill",
"ri-checkbox-circle-line",
"ri-checkbox-fill",
"ri-checkbox-indeterminate-fill",
"ri-checkbox-indeterminate-line",
"ri-checkbox-line",
"ri-checkbox-multiple-blank-fill",
"ri-checkbox-multiple-blank-line",
"ri-checkbox-multiple-fill",
"ri-checkbox-multiple-line",
"ri-china-railway-fill",
"ri-china-railway-line",
"ri-chrome-fill",
"ri-chrome-line",
"ri-clapperboard-fill",
"ri-clapperboard-line",
"ri-clipboard-fill",
"ri-clipboard-line",
"ri-clockwise-2-fill",
"ri-clockwise-2-line",
"ri-clockwise-fill",
"ri-clockwise-line",
"ri-close-circle-fill",
"ri-close-circle-line",
"ri-close-fill",
"ri-close-line",
"ri-closed-captioning-fill",
"ri-closed-captioning-line",
"ri-cloud-fill",
"ri-cloud-line",
"ri-cloud-off-fill",
"ri-cloud-off-line",
"ri-cloud-windy-fill",
"ri-cloud-windy-line",
"ri-cloudy-2-fill",
"ri-cloudy-2-line",
"ri-cloudy-fill",
"ri-cloudy-line",
"ri-code-box-fill",
"ri-code-box-line",
"ri-code-fill",
"ri-code-line",
"ri-code-s-fill",
"ri-code-s-line",
"ri-code-s-slash-fill",
"ri-code-s-slash-line",
"ri-code-view",
"ri-codepen-fill",
"ri-codepen-line",
"ri-coin-fill",
"ri-coin-line",
"ri-coins-fill",
"ri-coins-line",
"ri-collage-fill",
"ri-collage-line",
"ri-command-fill",
"ri-command-line",
"ri-community-fill",
"ri-community-line",
"ri-compass-2-fill",
"ri-compass-2-line",
"ri-compass-3-fill",
"ri-compass-3-line",
"ri-compass-4-fill",
"ri-compass-4-line",
"ri-compass-discover-fill",
"ri-compass-discover-line",
"ri-compass-fill",
"ri-compass-line",
"ri-compasses-2-fill",
"ri-compasses-2-line",
"ri-compasses-fill",
"ri-compasses-line",
"ri-computer-fill",
"ri-computer-line",
"ri-contacts-book-2-fill",
"ri-contacts-book-2-line",
"ri-contacts-book-fill",
"ri-contacts-book-line",
"ri-contacts-book-upload-fill",
"ri-contacts-book-upload-line",
"ri-contacts-fill",
"ri-contacts-line",
"ri-contrast-2-fill",
"ri-contrast-2-line",
"ri-contrast-drop-2-fill",
"ri-contrast-drop-2-line",
"ri-contrast-drop-fill",
"ri-contrast-drop-line",
"ri-contrast-fill",
"ri-contrast-line",
"ri-copper-coin-fill",
"ri-copper-coin-line",
"ri-copper-diamond-fill",
"ri-copper-diamond-line",
"ri-copyleft-fill",
"ri-copyleft-line",
"ri-copyright-fill",
"ri-copyright-line",
"ri-coreos-fill",
"ri-coreos-line",
"ri-coupon-2-fill",
"ri-coupon-2-line",
"ri-coupon-3-fill",
"ri-coupon-3-line",
"ri-coupon-4-fill",
"ri-coupon-4-line",
"ri-coupon-5-fill",
"ri-coupon-5-line",
"ri-coupon-fill",
"ri-coupon-line",
"ri-cpu-fill",
"ri-cpu-line",
"ri-creative-commons-by-fill",
"ri-creative-commons-by-line",
"ri-creative-commons-fill",
"ri-creative-commons-line",
"ri-creative-commons-nc-fill",
"ri-creative-commons-nc-line",
"ri-creative-commons-nd-fill",
"ri-creative-commons-nd-line",
"ri-creative-commons-sa-fill",
"ri-creative-commons-sa-line",
"ri-creative-commons-zero-fill",
"ri-creative-commons-zero-line",
"ri-criminal-fill",
"ri-criminal-line",
"ri-crop-2-fill",
"ri-crop-2-line",
"ri-crop-fill",
"ri-crop-line",
"ri-css3-fill",
"ri-css3-line",
"ri-cup-fill",
"ri-cup-line",
"ri-currency-fill",
"ri-currency-line",
"ri-cursor-fill",
"ri-cursor-line",
"ri-customer-service-2-fill",
"ri-customer-service-2-line",
"ri-customer-service-fill",
"ri-customer-service-line",
"ri-dashboard-2-fill",
"ri-dashboard-2-line",
"ri-dashboard-3-fill",
"ri-dashboard-3-line",
"ri-dashboard-fill",
"ri-dashboard-line",
"ri-database-2-fill",
"ri-database-2-line",
"ri-database-fill",
"ri-database-line",
"ri-delete-back-2-fill",
"ri-delete-back-2-line",
"ri-delete-back-fill",
"ri-delete-back-line",
"ri-delete-bin-2-fill",
"ri-delete-bin-2-line",
"ri-delete-bin-3-fill",
"ri-delete-bin-3-line",
"ri-delete-bin-4-fill",
"ri-delete-bin-4-line",
"ri-delete-bin-5-fill",
"ri-delete-bin-5-line",
"ri-delete-bin-6-fill",
"ri-delete-bin-6-line",
"ri-delete-bin-7-fill",
"ri-delete-bin-7-line",
"ri-delete-bin-fill",
"ri-delete-bin-line",
"ri-delete-column",
"ri-delete-row",
"ri-device-fill",
"ri-device-line",
"ri-device-recover-fill",
"ri-device-recover-line",
"ri-dingding-fill",
"ri-dingding-line",
"ri-direction-fill",
"ri-direction-line",
"ri-disc-fill",
"ri-disc-line",
"ri-discord-fill",
"ri-discord-line",
"ri-discuss-fill",
"ri-discuss-line",
"ri-dislike-fill",
"ri-dislike-line",
"ri-disqus-fill",
"ri-disqus-line",
"ri-divide-fill",
"ri-divide-line",
"ri-donut-chart-fill",
"ri-donut-chart-line",
"ri-door-closed-fill",
"ri-door-closed-line",
"ri-door-fill",
"ri-door-line",
"ri-door-lock-box-fill",
"ri-door-lock-box-line",
"ri-door-lock-fill",
"ri-door-lock-line",
"ri-door-open-fill",
"ri-door-open-line",
"ri-dossier-fill",
"ri-dossier-line",
"ri-douban-fill",
"ri-douban-line",
"ri-double-quotes-l",
"ri-double-quotes-r",
"ri-download-2-fill",
"ri-download-2-line",
"ri-download-cloud-2-fill",
"ri-download-cloud-2-line",
"ri-download-cloud-fill",
"ri-download-cloud-line",
"ri-download-fill",
"ri-download-line",
"ri-draft-fill",
"ri-draft-line",
"ri-drag-drop-fill",
"ri-drag-drop-line",
"ri-drag-move-2-fill",
"ri-drag-move-2-line",
"ri-drag-move-fill",
"ri-drag-move-line",
"ri-dribbble-fill",
"ri-dribbble-line",
"ri-drive-fill",
"ri-drive-line",
"ri-drizzle-fill",
"ri-drizzle-line",
"ri-drop-fill",
"ri-drop-line",
"ri-dropbox-fill",
"ri-dropbox-line",
"ri-dual-sim-1-fill",
"ri-dual-sim-1-line",
"ri-dual-sim-2-fill",
"ri-dual-sim-2-line",
"ri-dv-fill",
"ri-dv-line",
"ri-dvd-fill",
"ri-dvd-line",
"ri-e-bike-2-fill",
"ri-e-bike-2-line",
"ri-e-bike-fill",
"ri-e-bike-line",
"ri-earth-fill",
"ri-earth-line",
"ri-earthquake-fill",
"ri-earthquake-line",
"ri-edge-fill",
"ri-edge-line",
"ri-edit-2-fill",
"ri-edit-2-line",
"ri-edit-box-fill",
"ri-edit-box-line",
"ri-edit-circle-fill",
"ri-edit-circle-line",
"ri-edit-fill",
"ri-edit-line",
"ri-eject-fill",
"ri-eject-line",
"ri-emotion-2-fill",
"ri-emotion-2-line",
"ri-emotion-fill",
"ri-emotion-happy-fill",
"ri-emotion-happy-line",
"ri-emotion-laugh-fill",
"ri-emotion-laugh-line",
"ri-emotion-line",
"ri-emotion-normal-fill",
"ri-emotion-normal-line",
"ri-emotion-sad-fill",
"ri-emotion-sad-line",
"ri-emotion-unhappy-fill",
"ri-emotion-unhappy-line",
"ri-empathize-fill",
"ri-empathize-line",
"ri-emphasis-cn",
"ri-emphasis",
"ri-english-input",
"ri-equalizer-fill",
"ri-equalizer-line",
"ri-eraser-fill",
"ri-eraser-line",
"ri-error-warning-fill",
"ri-error-warning-line",
"ri-evernote-fill",
"ri-evernote-line",
"ri-exchange-box-fill",
"ri-exchange-box-line",
"ri-exchange-cny-fill",
"ri-exchange-cny-line",
"ri-exchange-dollar-fill",
"ri-exchange-dollar-line",
"ri-exchange-fill",
"ri-exchange-funds-fill",
"ri-exchange-funds-line",
"ri-exchange-line",
"ri-external-link-fill",
"ri-external-link-line",
"ri-eye-2-fill",
"ri-eye-2-line",
"ri-eye-close-fill",
"ri-eye-close-line",
"ri-eye-fill",
"ri-eye-line",
"ri-eye-off-fill",
"ri-eye-off-line",
"ri-facebook-box-fill",
"ri-facebook-box-line",
"ri-facebook-circle-fill",
"ri-facebook-circle-line",
"ri-facebook-fill",
"ri-facebook-line",
"ri-fahrenheit-fill",
"ri-fahrenheit-line",
"ri-feedback-fill",
"ri-feedback-line",
"ri-file-2-fill",
"ri-file-2-line",
"ri-file-3-fill",
"ri-file-3-line",
"ri-file-4-fill",
"ri-file-4-line",
"ri-file-add-fill",
"ri-file-add-line",
"ri-file-chart-2-fill",
"ri-file-chart-2-line",
"ri-file-chart-fill",
"ri-file-chart-line",
"ri-file-cloud-fill",
"ri-file-cloud-line",
"ri-file-code-fill",
"ri-file-code-line",
"ri-file-copy-2-fill",
"ri-file-copy-2-line",
"ri-file-copy-fill",
"ri-file-copy-line",
"ri-file-damage-fill",
"ri-file-damage-line",
"ri-file-download-fill",
"ri-file-download-line",
"ri-file-edit-fill",
"ri-file-edit-line",
"ri-file-excel-2-fill",
"ri-file-excel-2-line",
"ri-file-excel-fill",
"ri-file-excel-line",
"ri-file-fill",
"ri-file-forbid-fill",
"ri-file-forbid-line",
"ri-file-gif-fill",
"ri-file-gif-line",
"ri-file-history-fill",
"ri-file-history-line",
"ri-file-hwp-fill",
"ri-file-hwp-line",
"ri-file-info-fill",
"ri-file-info-line",
"ri-file-line",
"ri-file-list-2-fill",
"ri-file-list-2-line",
"ri-file-list-3-fill",
"ri-file-list-3-line",
"ri-file-list-fill",
"ri-file-list-line",
"ri-file-lock-fill",
"ri-file-lock-line",
"ri-file-marked-fill",
"ri-file-marked-line",
"ri-file-music-fill",
"ri-file-music-line",
"ri-file-paper-2-fill",
"ri-file-paper-2-line",
"ri-file-paper-fill",
"ri-file-paper-line",
"ri-file-pdf-fill",
"ri-file-pdf-line",
"ri-file-ppt-2-fill",
"ri-file-ppt-2-line",
"ri-file-ppt-fill",
"ri-file-ppt-line",
"ri-file-reduce-fill",
"ri-file-reduce-line",
"ri-file-search-fill",
"ri-file-search-line",
"ri-file-settings-fill",
"ri-file-settings-line",
"ri-file-shield-2-fill",
"ri-file-shield-2-line",
"ri-file-shield-fill",
"ri-file-shield-line",
"ri-file-shred-fill",
"ri-file-shred-line",
"ri-file-text-fill",
"ri-file-text-line",
"ri-file-transfer-fill",
"ri-file-transfer-line",
"ri-file-unknow-fill",
"ri-file-unknow-line",
"ri-file-upload-fill",
"ri-file-upload-line",
"ri-file-user-fill",
"ri-file-user-line",
"ri-file-warning-fill",
"ri-file-warning-line",
"ri-file-word-2-fill",
"ri-file-word-2-line",
"ri-file-word-fill",
"ri-file-word-line",
"ri-file-zip-fill",
"ri-file-zip-line",
"ri-film-fill",
"ri-film-line",
"ri-filter-2-fill",
"ri-filter-2-line",
"ri-filter-3-fill",
"ri-filter-3-line",
"ri-filter-fill",
"ri-filter-line",
"ri-filter-off-fill",
"ri-filter-off-line",
"ri-find-replace-fill",
"ri-find-replace-line",
"ri-finder-fill",
"ri-finder-line",
"ri-fingerprint-2-fill",
"ri-fingerprint-2-line",
"ri-fingerprint-fill",
"ri-fingerprint-line",
"ri-fire-fill",
"ri-fire-line",
"ri-firefox-fill",
"ri-firefox-line",
"ri-first-aid-kit-fill",
"ri-first-aid-kit-line",
"ri-flag-2-fill",
"ri-flag-2-line",
"ri-flag-fill",
"ri-flag-line",
"ri-flashlight-fill",
"ri-flashlight-line",
"ri-flask-fill",
"ri-flask-line",
"ri-flight-land-fill",
"ri-flight-land-line",
"ri-flight-takeoff-fill",
"ri-flight-takeoff-line",
"ri-flood-fill",
"ri-flood-line",
"ri-flow-chart",
"ri-flutter-fill",
"ri-flutter-line",
"ri-focus-2-fill",
"ri-focus-2-line",
"ri-focus-3-fill",
"ri-focus-3-line",
"ri-focus-fill",
"ri-focus-line",
"ri-foggy-fill",
"ri-foggy-line",
"ri-folder-2-fill",
"ri-folder-2-line",
"ri-folder-3-fill",
"ri-folder-3-line",
"ri-folder-4-fill",
"ri-folder-4-line",
"ri-folder-5-fill",
"ri-folder-5-line",
"ri-folder-add-fill",
"ri-folder-add-line",
"ri-folder-chart-2-fill",
"ri-folder-chart-2-line",
"ri-folder-chart-fill",
"ri-folder-chart-line",
"ri-folder-download-fill",
"ri-folder-download-line",
"ri-folder-fill",
"ri-folder-forbid-fill",
"ri-folder-forbid-line",
"ri-folder-history-fill",
"ri-folder-history-line",
"ri-folder-info-fill",
"ri-folder-info-line",
"ri-folder-keyhole-fill",
"ri-folder-keyhole-line",
"ri-folder-line",
"ri-folder-lock-fill",
"ri-folder-lock-line",
"ri-folder-music-fill",
"ri-folder-music-line",
"ri-folder-open-fill",
"ri-folder-open-line",
"ri-folder-received-fill",
"ri-folder-received-line",
"ri-folder-reduce-fill",
"ri-folder-reduce-line",
"ri-folder-settings-fill",
"ri-folder-settings-line",
"ri-folder-shared-fill",
"ri-folder-shared-line",
"ri-folder-shield-2-fill",
"ri-folder-shield-2-line",
"ri-folder-shield-fill",
"ri-folder-shield-line",
"ri-folder-transfer-fill",
"ri-folder-transfer-line",
"ri-folder-unknow-fill",
"ri-folder-unknow-line",
"ri-folder-upload-fill",
"ri-folder-upload-line",
"ri-folder-user-fill",
"ri-folder-user-line",
"ri-folder-warning-fill",
"ri-folder-warning-line",
"ri-folder-zip-fill",
"ri-folder-zip-line",
"ri-folders-fill",
"ri-folders-line",
"ri-font-color",
"ri-font-size-2",
"ri-font-size",
"ri-football-fill",
"ri-football-line",
"ri-footprint-fill",
"ri-footprint-line",
"ri-forbid-2-fill",
"ri-forbid-2-line",
"ri-forbid-fill",
"ri-forbid-line",
"ri-format-clear",
"ri-fridge-fill",
"ri-fridge-line",
"ri-fullscreen-exit-fill",
"ri-fullscreen-exit-line",
"ri-fullscreen-fill",
"ri-fullscreen-line",
"ri-function-fill",
"ri-function-line",
"ri-functions",
"ri-funds-box-fill",
"ri-funds-box-line",
"ri-funds-fill",
"ri-funds-line",
"ri-gallery-fill",
"ri-gallery-line",
"ri-gallery-upload-fill",
"ri-gallery-upload-line",
"ri-game-fill",
"ri-game-line",
"ri-gamepad-fill",
"ri-gamepad-line",
"ri-gas-station-fill",
"ri-gas-station-line",
"ri-gatsby-fill",
"ri-gatsby-line",
"ri-genderless-fill",
"ri-genderless-line",
"ri-ghost-2-fill",
"ri-ghost-2-line",
"ri-ghost-fill",
"ri-ghost-line",
"ri-ghost-smile-fill",
"ri-ghost-smile-line",
"ri-gift-2-fill",
"ri-gift-2-line",
"ri-gift-fill",
"ri-gift-line",
"ri-git-branch-fill",
"ri-git-branch-line",
"ri-git-commit-fill",
"ri-git-commit-line",
"ri-git-merge-fill",
"ri-git-merge-line",
"ri-git-pull-request-fill",
"ri-git-pull-request-line",
"ri-git-repository-commits-fill",
"ri-git-repository-commits-line",
"ri-git-repository-fill",
"ri-git-repository-line",
"ri-git-repository-private-fill",
"ri-git-repository-private-line",
"ri-github-fill",
"ri-github-line",
"ri-gitlab-fill",
"ri-gitlab-line",
"ri-global-fill",
"ri-global-line",
"ri-globe-fill",
"ri-globe-line",
"ri-goblet-fill",
"ri-goblet-line",
"ri-google-fill",
"ri-google-line",
"ri-google-play-fill",
"ri-google-play-line",
"ri-government-fill",
"ri-government-line",
"ri-gps-fill",
"ri-gps-line",
"ri-gradienter-fill",
"ri-gradienter-line",
"ri-grid-fill",
"ri-grid-line",
"ri-group-2-fill",
"ri-group-2-line",
"ri-group-fill",
"ri-group-line",
"ri-guide-fill",
"ri-guide-line",
"ri-h-1",
"ri-h-2",
"ri-h-3",
"ri-h-4",
"ri-h-5",
"ri-h-6",
"ri-hail-fill",
"ri-hail-line",
"ri-hammer-fill",
"ri-hammer-line",
"ri-hand-coin-fill",
"ri-hand-coin-line",
"ri-hand-heart-fill",
"ri-hand-heart-line",
"ri-hand-sanitizer-fill",
"ri-hand-sanitizer-line",
"ri-handbag-fill",
"ri-handbag-line",
"ri-hard-drive-2-fill",
"ri-hard-drive-2-line",
"ri-hard-drive-fill",
"ri-hard-drive-line",
"ri-hashtag",
"ri-haze-2-fill",
"ri-haze-2-line",
"ri-haze-fill",
"ri-haze-line",
"ri-hd-fill",
"ri-hd-line",
"ri-heading",
"ri-headphone-fill",
"ri-headphone-line",
"ri-health-book-fill",
"ri-health-book-line",
"ri-heart-2-fill",
"ri-heart-2-line",
"ri-heart-3-fill",
"ri-heart-3-line",
"ri-heart-add-fill",
"ri-heart-add-line",
"ri-heart-fill",
"ri-heart-line",
"ri-heart-pulse-fill",
"ri-heart-pulse-line",
"ri-hearts-fill",
"ri-hearts-line",
"ri-heavy-showers-fill",
"ri-heavy-showers-line",
"ri-history-fill",
"ri-history-line",
"ri-home-2-fill",
"ri-home-2-line",
"ri-home-3-fill",
"ri-home-3-line",
"ri-home-4-fill",
"ri-home-4-line",
"ri-home-5-fill",
"ri-home-5-line",
"ri-home-6-fill",
"ri-home-6-line",
"ri-home-7-fill",
"ri-home-7-line",
"ri-home-8-fill",
"ri-home-8-line",
"ri-home-fill",
"ri-home-gear-fill",
"ri-home-gear-line",
"ri-home-heart-fill",
"ri-home-heart-line",
"ri-home-line",
"ri-home-smile-2-fill",
"ri-home-smile-2-line",
"ri-home-smile-fill",
"ri-home-smile-line",
"ri-home-wifi-fill",
"ri-home-wifi-line",
"ri-honor-of-kings-fill",
"ri-honor-of-kings-line",
"ri-honour-fill",
"ri-honour-line",
"ri-hospital-fill",
"ri-hospital-line",
"ri-hotel-bed-fill",
"ri-hotel-bed-line",
"ri-hotel-fill",
"ri-hotel-line",
"ri-hotspot-fill",
"ri-hotspot-line",
"ri-hq-fill",
"ri-hq-line",
"ri-html5-fill",
"ri-html5-line",
"ri-ie-fill",
"ri-ie-line",
"ri-image-2-fill",
"ri-image-2-line",
"ri-image-add-fill",
"ri-image-add-line",
"ri-image-edit-fill",
"ri-image-edit-line",
"ri-image-fill",
"ri-image-line",
"ri-inbox-archive-fill",
"ri-inbox-archive-line",
"ri-inbox-fill",
"ri-inbox-line",
"ri-inbox-unarchive-fill",
"ri-inbox-unarchive-line",
"ri-increase-decrease-fill",
"ri-increase-decrease-line",
"ri-indent-decrease",
"ri-indent-increase",
"ri-indeterminate-circle-fill",
"ri-indeterminate-circle-line",
"ri-information-fill",
"ri-information-line",
"ri-infrared-thermometer-fill",
"ri-infrared-thermometer-line",
"ri-ink-bottle-fill",
"ri-ink-bottle-line",
"ri-input-cursor-move",
"ri-input-method-fill",
"ri-input-method-line",
"ri-insert-column-left",
"ri-insert-column-right",
"ri-insert-row-bottom",
"ri-insert-row-top",
"ri-instagram-fill",
"ri-instagram-line",
"ri-install-fill",
"ri-install-line",
"ri-invision-fill",
"ri-invision-line",
"ri-italic",
"ri-kakao-talk-fill",
"ri-kakao-talk-line",
"ri-key-2-fill",
"ri-key-2-line",
"ri-key-fill",
"ri-key-line",
"ri-keyboard-box-fill",
"ri-keyboard-box-line",
"ri-keyboard-fill",
"ri-keyboard-line",
"ri-keynote-fill",
"ri-keynote-line",
"ri-knife-blood-fill",
"ri-knife-blood-line",
"ri-knife-fill",
"ri-knife-line",
"ri-landscape-fill",
"ri-landscape-line",
"ri-layout-2-fill",
"ri-layout-2-line",
"ri-layout-3-fill",
"ri-layout-3-line",
"ri-layout-4-fill",
"ri-layout-4-line",
"ri-layout-5-fill",
"ri-layout-5-line",
"ri-layout-6-fill",
"ri-layout-6-line",
"ri-layout-bottom-2-fill",
"ri-layout-bottom-2-line",
"ri-layout-bottom-fill",
"ri-layout-bottom-line",
"ri-layout-column-fill",
"ri-layout-column-line",
"ri-layout-fill",
"ri-layout-grid-fill",
"ri-layout-grid-line",
"ri-layout-left-2-fill",
"ri-layout-left-2-line",
"ri-layout-left-fill",
"ri-layout-left-line",
"ri-layout-line",
"ri-layout-masonry-fill",
"ri-layout-masonry-line",
"ri-layout-right-2-fill",
"ri-layout-right-2-line",
"ri-layout-right-fill",
"ri-layout-right-line",
"ri-layout-row-fill",
"ri-layout-row-line",
"ri-layout-top-2-fill",
"ri-layout-top-2-line",
"ri-layout-top-fill",
"ri-layout-top-line",
"ri-leaf-fill",
"ri-leaf-line",
"ri-lifebuoy-fill",
"ri-lifebuoy-line",
"ri-lightbulb-fill",
"ri-lightbulb-flash-fill",
"ri-lightbulb-flash-line",
"ri-lightbulb-line",
"ri-line-chart-fill",
"ri-line-chart-line",
"ri-line-fill",
"ri-line-height",
"ri-line-line",
"ri-link-m",
"ri-link-unlink-m",
"ri-link-unlink",
"ri-link",
"ri-linkedin-box-fill",
"ri-linkedin-box-line",
"ri-linkedin-fill",
"ri-linkedin-line",
"ri-links-fill",
"ri-links-line",
"ri-list-check-2",
"ri-list-check",
"ri-list-ordered",
"ri-list-settings-fill",
"ri-list-settings-line",
"ri-list-unordered",
"ri-live-fill",
"ri-live-line",
"ri-loader-2-fill",
"ri-loader-2-line",
"ri-loader-3-fill",
"ri-loader-3-line",
"ri-loader-4-fill",
"ri-loader-4-line",
"ri-loader-5-fill",
"ri-loader-5-line",
"ri-loader-fill",
"ri-loader-line",
"ri-lock-2-fill",
"ri-lock-2-line",
"ri-lock-fill",
"ri-lock-line",
"ri-lock-password-fill",
"ri-lock-password-line",
"ri-lock-unlock-fill",
"ri-lock-unlock-line",
"ri-login-box-fill",
"ri-login-box-line",
"ri-login-circle-fill",
"ri-login-circle-line",
"ri-logout-box-fill",
"ri-logout-box-line",
"ri-logout-box-r-fill",
"ri-logout-box-r-line",
"ri-logout-circle-fill",
"ri-logout-circle-line",
"ri-logout-circle-r-fill",
"ri-logout-circle-r-line",
"ri-luggage-cart-fill",
"ri-luggage-cart-line",
"ri-luggage-deposit-fill",
"ri-luggage-deposit-line",
"ri-lungs-fill",
"ri-lungs-line",
"ri-mac-fill",
"ri-mac-line",
"ri-macbook-fill",
"ri-macbook-line",
"ri-magic-fill",
"ri-magic-line",
"ri-mail-add-fill",
"ri-mail-add-line",
"ri-mail-check-fill",
"ri-mail-check-line",
"ri-mail-close-fill",
"ri-mail-close-line",
"ri-mail-download-fill",
"ri-mail-download-line",
"ri-mail-fill",
"ri-mail-forbid-fill",
"ri-mail-forbid-line",
"ri-mail-line",
"ri-mail-lock-fill",
"ri-mail-lock-line",
"ri-mail-open-fill",
"ri-mail-open-line",
"ri-mail-send-fill",
"ri-mail-send-line",
"ri-mail-settings-fill",
"ri-mail-settings-line",
"ri-mail-star-fill",
"ri-mail-star-line",
"ri-mail-unread-fill",
"ri-mail-unread-line",
"ri-mail-volume-fill",
"ri-mail-volume-line",
"ri-map-2-fill",
"ri-map-2-line",
"ri-map-fill",
"ri-map-line",
"ri-map-pin-2-fill",
"ri-map-pin-2-line",
"ri-map-pin-3-fill",
"ri-map-pin-3-line",
"ri-map-pin-4-fill",
"ri-map-pin-4-line",
"ri-map-pin-5-fill",
"ri-map-pin-5-line",
"ri-map-pin-add-fill",
"ri-map-pin-add-line",
"ri-map-pin-fill",
"ri-map-pin-line",
"ri-map-pin-range-fill",
"ri-map-pin-range-line",
"ri-map-pin-time-fill",
"ri-map-pin-time-line",
"ri-map-pin-user-fill",
"ri-map-pin-user-line",
"ri-mark-pen-fill",
"ri-mark-pen-line",
"ri-markdown-fill",
"ri-markdown-line",
"ri-markup-fill",
"ri-markup-line",
"ri-mastercard-fill",
"ri-mastercard-line",
"ri-mastodon-fill",
"ri-mastodon-line",
"ri-medal-2-fill",
"ri-medal-2-line",
"ri-medal-fill",
"ri-medal-line",
"ri-medicine-bottle-fill",
"ri-medicine-bottle-line",
"ri-medium-fill",
"ri-medium-line",
"ri-men-fill",
"ri-men-line",
"ri-mental-health-fill",
"ri-mental-health-line",
"ri-menu-2-fill",
"ri-menu-2-line",
"ri-menu-3-fill",
"ri-menu-3-line",
"ri-menu-4-fill",
"ri-menu-4-line",
"ri-menu-5-fill",
"ri-menu-5-line",
"ri-menu-add-fill",
"ri-menu-add-line",
"ri-menu-fill",
"ri-menu-fold-fill",
"ri-menu-fold-line",
"ri-menu-line",
"ri-menu-unfold-fill",
"ri-menu-unfold-line",
"ri-merge-cells-horizontal",
"ri-merge-cells-vertical",
"ri-message-2-fill",
"ri-message-2-line",
"ri-message-3-fill",
"ri-message-3-line",
"ri-message-fill",
"ri-message-line",
"ri-messenger-fill",
"ri-messenger-line",
"ri-meteor-fill",
"ri-meteor-line",
"ri-mic-2-fill",
"ri-mic-2-line",
"ri-mic-fill",
"ri-mic-line",
"ri-mic-off-fill",
"ri-mic-off-line",
"ri-mickey-fill",
"ri-mickey-line",
"ri-microscope-fill",
"ri-microscope-line",
"ri-microsoft-fill",
"ri-microsoft-line",
"ri-mind-map",
"ri-mini-program-fill",
"ri-mini-program-line",
"ri-mist-fill",
"ri-mist-line",
"ri-money-cny-box-fill",
"ri-money-cny-box-line",
"ri-money-cny-circle-fill",
"ri-money-cny-circle-line",
"ri-money-dollar-box-fill",
"ri-money-dollar-box-line",
"ri-money-dollar-circle-fill",
"ri-money-dollar-circle-line",
"ri-money-euro-box-fill",
"ri-money-euro-box-line",
"ri-money-euro-circle-fill",
"ri-money-euro-circle-line",
"ri-money-pound-box-fill",
"ri-money-pound-box-line",
"ri-money-pound-circle-fill",
"ri-money-pound-circle-line",
"ri-moon-clear-fill",
"ri-moon-clear-line",
"ri-moon-cloudy-fill",
"ri-moon-cloudy-line",
"ri-moon-fill",
"ri-moon-foggy-fill",
"ri-moon-foggy-line",
"ri-moon-line",
"ri-more-2-fill",
"ri-more-2-line",
"ri-more-fill",
"ri-more-line",
"ri-motorbike-fill",
"ri-motorbike-line",
"ri-mouse-fill",
"ri-mouse-line",
"ri-movie-2-fill",
"ri-movie-2-line",
"ri-movie-fill",
"ri-movie-line",
"ri-music-2-fill",
"ri-music-2-line",
"ri-music-fill",
"ri-music-line",
"ri-mv-fill",
"ri-mv-line",
"ri-navigation-fill",
"ri-navigation-line",
"ri-netease-cloud-music-fill",
"ri-netease-cloud-music-line",
"ri-netflix-fill",
"ri-netflix-line",
"ri-newspaper-fill",
"ri-newspaper-line",
"ri-node-tree",
"ri-notification-2-fill",
"ri-notification-2-line",
"ri-notification-3-fill",
"ri-notification-3-line",
"ri-notification-4-fill",
"ri-notification-4-line",
"ri-notification-badge-fill",
"ri-notification-badge-line",
"ri-notification-fill",
"ri-notification-line",
"ri-notification-off-fill",
"ri-notification-off-line",
"ri-npmjs-fill",
"ri-npmjs-line",
"ri-number-0",
"ri-number-1",
"ri-number-2",
"ri-number-3",
"ri-number-4",
"ri-number-5",
"ri-number-6",
"ri-number-7",
"ri-number-8",
"ri-number-9",
"ri-numbers-fill",
"ri-numbers-line",
"ri-nurse-fill",
"ri-nurse-line",
"ri-oil-fill",
"ri-oil-line",
"ri-omega",
"ri-open-arm-fill",
"ri-open-arm-line",
"ri-open-source-fill",
"ri-open-source-line",
"ri-opera-fill",
"ri-opera-line",
"ri-order-play-fill",
"ri-order-play-line",
"ri-organization-chart",
"ri-outlet-2-fill",
"ri-outlet-2-line",
"ri-outlet-fill",
"ri-outlet-line",
"ri-page-separator",
"ri-pages-fill",
"ri-pages-line",
"ri-paint-brush-fill",
"ri-paint-brush-line",
"ri-paint-fill",
"ri-paint-line",
"ri-palette-fill",
"ri-palette-line",
"ri-pantone-fill",
"ri-pantone-line",
"ri-paragraph",
"ri-parent-fill",
"ri-parent-line",
"ri-parentheses-fill",
"ri-parentheses-line",
"ri-parking-box-fill",
"ri-parking-box-line",
"ri-parking-fill",
"ri-parking-line",
"ri-passport-fill",
"ri-passport-line",
"ri-patreon-fill",
"ri-patreon-line",
"ri-pause-circle-fill",
"ri-pause-circle-line",
"ri-pause-fill",
"ri-pause-line",
"ri-pause-mini-fill",
"ri-pause-mini-line",
"ri-paypal-fill",
"ri-paypal-line",
"ri-pen-nib-fill",
"ri-pen-nib-line",
"ri-pencil-fill",
"ri-pencil-line",
"ri-pencil-ruler-2-fill",
"ri-pencil-ruler-2-line",
"ri-pencil-ruler-fill",
"ri-pencil-ruler-line",
"ri-percent-fill",
"ri-percent-line",
"ri-phone-camera-fill",
"ri-phone-camera-line",
"ri-phone-fill",
"ri-phone-find-fill",
"ri-phone-find-line",
"ri-phone-line",
"ri-phone-lock-fill",
"ri-phone-lock-line",
"ri-picture-in-picture-2-fill",
"ri-picture-in-picture-2-line",
"ri-picture-in-picture-exit-fill",
"ri-picture-in-picture-exit-line",
"ri-picture-in-picture-fill",
"ri-picture-in-picture-line",
"ri-pie-chart-2-fill",
"ri-pie-chart-2-line",
"ri-pie-chart-box-fill",
"ri-pie-chart-box-line",
"ri-pie-chart-fill",
"ri-pie-chart-line",
"ri-pin-distance-fill",
"ri-pin-distance-line",
"ri-ping-pong-fill",
"ri-ping-pong-line",
"ri-pinterest-fill",
"ri-pinterest-line",
"ri-pinyin-input",
"ri-pixelfed-fill",
"ri-pixelfed-line",
"ri-plane-fill",
"ri-plane-line",
"ri-plant-fill",
"ri-plant-line",
"ri-play-circle-fill",
"ri-play-circle-line",
"ri-play-fill",
"ri-play-line",
"ri-play-list-2-fill",
"ri-play-list-2-line",
"ri-play-list-add-fill",
"ri-play-list-add-line",
"ri-play-list-fill",
"ri-play-list-line",
"ri-play-mini-fill",
"ri-play-mini-line",
"ri-playstation-fill",
"ri-playstation-line",
"ri-plug-2-fill",
"ri-plug-2-line",
"ri-plug-fill",
"ri-plug-line",
"ri-polaroid-2-fill",
"ri-polaroid-2-line",
"ri-polaroid-fill",
"ri-polaroid-line",
"ri-police-car-fill",
"ri-police-car-line",
"ri-price-tag-2-fill",
"ri-price-tag-2-line",
"ri-price-tag-3-fill",
"ri-price-tag-3-line",
"ri-price-tag-fill",
"ri-price-tag-line",
"ri-printer-cloud-fill",
"ri-printer-cloud-line",
"ri-printer-fill",
"ri-printer-line",
"ri-product-hunt-fill",
"ri-product-hunt-line",
"ri-profile-fill",
"ri-profile-line",
"ri-projector-2-fill",
"ri-projector-2-line",
"ri-projector-fill",
"ri-projector-line",
"ri-psychotherapy-fill",
"ri-psychotherapy-line",
"ri-pulse-fill",
"ri-pulse-line",
"ri-pushpin-2-fill",
"ri-pushpin-2-line",
"ri-pushpin-fill",
"ri-pushpin-line",
"ri-qq-fill",
"ri-qq-line",
"ri-qr-code-fill",
"ri-qr-code-line",
"ri-qr-scan-2-fill",
"ri-qr-scan-2-line",
"ri-qr-scan-fill",
"ri-qr-scan-line",
"ri-question-answer-fill",
"ri-question-answer-line",
"ri-question-fill",
"ri-question-line",
"ri-question-mark",
"ri-questionnaire-fill",
"ri-questionnaire-line",
"ri-quill-pen-fill",
"ri-quill-pen-line",
"ri-radar-fill",
"ri-radar-line",
"ri-radio-2-fill",
"ri-radio-2-line",
"ri-radio-button-fill",
"ri-radio-button-line",
"ri-radio-fill",
"ri-radio-line",
"ri-rainbow-fill",
"ri-rainbow-line",
"ri-rainy-fill",
"ri-rainy-line",
"ri-reactjs-fill",
"ri-reactjs-line",
"ri-record-circle-fill",
"ri-record-circle-line",
"ri-record-mail-fill",
"ri-record-mail-line",
"ri-recycle-fill",
"ri-recycle-line",
"ri-red-packet-fill",
"ri-red-packet-line",
"ri-reddit-fill",
"ri-reddit-line",
"ri-refresh-fill",
"ri-refresh-line",
"ri-refund-2-fill",
"ri-refund-2-line",
"ri-refund-fill",
"ri-refund-line",
"ri-registered-fill",
"ri-registered-line",
"ri-remixicon-fill",
"ri-remixicon-line",
"ri-remote-control-2-fill",
"ri-remote-control-2-line",
"ri-remote-control-fill",
"ri-remote-control-line",
"ri-repeat-2-fill",
"ri-repeat-2-line",
"ri-repeat-fill",
"ri-repeat-line",
"ri-repeat-one-fill",
"ri-repeat-one-line",
"ri-reply-all-fill",
"ri-reply-all-line",
"ri-reply-fill",
"ri-reply-line",
"ri-reserved-fill",
"ri-reserved-line",
"ri-rest-time-fill",
"ri-rest-time-line",
"ri-restart-fill",
"ri-restart-line",
"ri-restaurant-2-fill",
"ri-restaurant-2-line",
"ri-restaurant-fill",
"ri-restaurant-line",
"ri-rewind-fill",
"ri-rewind-line",
"ri-rewind-mini-fill",
"ri-rewind-mini-line",
"ri-rhythm-fill",
"ri-rhythm-line",
"ri-riding-fill",
"ri-riding-line",
"ri-road-map-fill",
"ri-road-map-line",
"ri-roadster-fill",
"ri-roadster-line",
"ri-robot-fill",
"ri-robot-line",
"ri-rocket-2-fill",
"ri-rocket-2-line",
"ri-rocket-fill",
"ri-rocket-line",
"ri-rotate-lock-fill",
"ri-rotate-lock-line",
"ri-rounded-corner",
"ri-route-fill",
"ri-route-line",
"ri-router-fill",
"ri-router-line",
"ri-rss-fill",
"ri-rss-line",
"ri-ruler-2-fill",
"ri-ruler-2-line",
"ri-ruler-fill",
"ri-ruler-line",
"ri-run-fill",
"ri-run-line",
"ri-safari-fill",
"ri-safari-line",
"ri-safe-2-fill",
"ri-safe-2-line",
"ri-safe-fill",
"ri-safe-line",
"ri-sailboat-fill",
"ri-sailboat-line",
"ri-save-2-fill",
"ri-save-2-line",
"ri-save-3-fill",
"ri-save-3-line",
"ri-save-fill",
"ri-save-line",
"ri-scales-2-fill",
"ri-scales-2-line",
"ri-scales-3-fill",
"ri-scales-3-line",
"ri-scales-fill",
"ri-scales-line",
"ri-scan-2-fill",
"ri-scan-2-line",
"ri-scan-fill",
"ri-scan-line",
"ri-scissors-2-fill",
"ri-scissors-2-line",
"ri-scissors-cut-fill",
"ri-scissors-cut-line",
"ri-scissors-fill",
"ri-scissors-line",
"ri-screenshot-2-fill",
"ri-screenshot-2-line",
"ri-screenshot-fill",
"ri-screenshot-line",
"ri-sd-card-fill",
"ri-sd-card-line",
"ri-sd-card-mini-fill",
"ri-sd-card-mini-line",
"ri-search-2-fill",
"ri-search-2-line",
"ri-search-eye-fill",
"ri-search-eye-line",
"ri-search-fill",
"ri-search-line",
"ri-secure-payment-fill",
"ri-secure-payment-line",
"ri-seedling-fill",
"ri-seedling-line",
"ri-send-backward",
"ri-send-plane-2-fill",
"ri-send-plane-2-line",
"ri-send-plane-fill",
"ri-send-plane-line",
"ri-send-to-back",
"ri-sensor-fill",
"ri-sensor-line",
"ri-separator",
"ri-server-fill",
"ri-server-line",
"ri-service-fill",
"ri-service-line",
"ri-settings-2-fill",
"ri-settings-2-line",
"ri-settings-3-fill",
"ri-settings-3-line",
"ri-settings-4-fill",
"ri-settings-4-line",
"ri-settings-5-fill",
"ri-settings-5-line",
"ri-settings-6-fill",
"ri-settings-6-line",
"ri-settings-fill",
"ri-settings-line",
"ri-shape-2-fill",
"ri-shape-2-line",
"ri-shape-fill",
"ri-shape-line",
"ri-share-box-fill",
"ri-share-box-line",
"ri-share-circle-fill",
"ri-share-circle-line",
"ri-share-fill",
"ri-share-forward-2-fill",
"ri-share-forward-2-line",
"ri-share-forward-box-fill",
"ri-share-forward-box-line",
"ri-share-forward-fill",
"ri-share-forward-line",
"ri-share-line",
"ri-shield-check-fill",
"ri-shield-check-line",
"ri-shield-cross-fill",
"ri-shield-cross-line",
"ri-shield-fill",
"ri-shield-flash-fill",
"ri-shield-flash-line",
"ri-shield-keyhole-fill",
"ri-shield-keyhole-line",
"ri-shield-line",
"ri-shield-star-fill",
"ri-shield-star-line",
"ri-shield-user-fill",
"ri-shield-user-line",
"ri-ship-2-fill",
"ri-ship-2-line",
"ri-ship-fill",
"ri-ship-line",
"ri-shirt-fill",
"ri-shirt-line",
"ri-shopping-bag-2-fill",
"ri-shopping-bag-2-line",
"ri-shopping-bag-3-fill",
"ri-shopping-bag-3-line",
"ri-shopping-bag-fill",
"ri-shopping-bag-line",
"ri-shopping-basket-2-fill",
"ri-shopping-basket-2-line",
"ri-shopping-basket-fill",
"ri-shopping-basket-line",
"ri-shopping-cart-2-fill",
"ri-shopping-cart-2-line",
"ri-shopping-cart-fill",
"ri-shopping-cart-line",
"ri-showers-fill",
"ri-showers-line",
"ri-shuffle-fill",
"ri-shuffle-line",
"ri-shut-down-fill",
"ri-shut-down-line",
"ri-side-bar-fill",
"ri-side-bar-line",
"ri-signal-tower-fill",
"ri-signal-tower-line",
"ri-signal-wifi-1-fill",
"ri-signal-wifi-1-line",
"ri-signal-wifi-2-fill",
"ri-signal-wifi-2-line",
"ri-signal-wifi-3-fill",
"ri-signal-wifi-3-line",
"ri-signal-wifi-error-fill",
"ri-signal-wifi-error-line",
"ri-signal-wifi-fill",
"ri-signal-wifi-line",
"ri-signal-wifi-off-fill",
"ri-signal-wifi-off-line",
"ri-sim-card-2-fill",
"ri-sim-card-2-line",
"ri-sim-card-fill",
"ri-sim-card-line",
"ri-single-quotes-l",
"ri-single-quotes-r",
"ri-sip-fill",
"ri-sip-line",
"ri-skip-back-fill",
"ri-skip-back-line",
"ri-skip-back-mini-fill",
"ri-skip-back-mini-line",
"ri-skip-forward-fill",
"ri-skip-forward-line",
"ri-skip-forward-mini-fill",
"ri-skip-forward-mini-line",
"ri-skull-2-fill",
"ri-skull-2-line",
"ri-skull-fill",
"ri-skull-line",
"ri-skype-fill",
"ri-skype-line",
"ri-slack-fill",
"ri-slack-line",
"ri-slice-fill",
"ri-slice-line",
"ri-slideshow-2-fill",
"ri-slideshow-2-line",
"ri-slideshow-3-fill",
"ri-slideshow-3-line",
"ri-slideshow-4-fill",
"ri-slideshow-4-line",
"ri-slideshow-fill",
"ri-slideshow-line",
"ri-smartphone-fill",
"ri-smartphone-line",
"ri-snapchat-fill",
"ri-snapchat-line",
"ri-snowy-fill",
"ri-snowy-line",
"ri-sort-asc",
"ri-sort-desc",
"ri-sound-module-fill",
"ri-sound-module-line",
"ri-soundcloud-fill",
"ri-soundcloud-line",
"ri-space-ship-fill",
"ri-space-ship-line",
"ri-space",
"ri-spam-2-fill",
"ri-spam-2-line",
"ri-spam-3-fill",
"ri-spam-3-line",
"ri-spam-fill",
"ri-spam-line",
"ri-speaker-2-fill",
"ri-speaker-2-line",
"ri-speaker-3-fill",
"ri-speaker-3-line",
"ri-speaker-fill",
"ri-speaker-line",
"ri-spectrum-fill",
"ri-spectrum-line",
"ri-speed-fill",
"ri-speed-line",
"ri-speed-mini-fill",
"ri-speed-mini-line",
"ri-split-cells-horizontal",
"ri-split-cells-vertical",
"ri-spotify-fill",
"ri-spotify-line",
"ri-spy-fill",
"ri-spy-line",
"ri-stack-fill",
"ri-stack-line",
"ri-stack-overflow-fill",
"ri-stack-overflow-line",
"ri-stackshare-fill",
"ri-stackshare-line",
"ri-star-fill",
"ri-star-half-fill",
"ri-star-half-line",
"ri-star-half-s-fill",
"ri-star-half-s-line",
"ri-star-line",
"ri-star-s-fill",
"ri-star-s-line",
"ri-star-smile-fill",
"ri-star-smile-line",
"ri-steam-fill",
"ri-steam-line",
"ri-steering-2-fill",
"ri-steering-2-line",
"ri-steering-fill",
"ri-steering-line",
"ri-stethoscope-fill",
"ri-stethoscope-line",
"ri-sticky-note-2-fill",
"ri-sticky-note-2-line",
"ri-sticky-note-fill",
"ri-sticky-note-line",
"ri-stock-fill",
"ri-stock-line",
"ri-stop-circle-fill",
"ri-stop-circle-line",
"ri-stop-fill",
"ri-stop-line",
"ri-stop-mini-fill",
"ri-stop-mini-line",
"ri-store-2-fill",
"ri-store-2-line",
"ri-store-3-fill",
"ri-store-3-line",
"ri-store-fill",
"ri-store-line",
"ri-strikethrough-2",
"ri-strikethrough",
"ri-subscript-2",
"ri-subscript",
"ri-subtract-fill",
"ri-subtract-line",
"ri-subway-fill",
"ri-subway-line",
"ri-subway-wifi-fill",
"ri-subway-wifi-line",
"ri-suitcase-2-fill",
"ri-suitcase-2-line",
"ri-suitcase-3-fill",
"ri-suitcase-3-line",
"ri-suitcase-fill",
"ri-suitcase-line",
"ri-sun-cloudy-fill",
"ri-sun-cloudy-line",
"ri-sun-fill",
"ri-sun-foggy-fill",
"ri-sun-foggy-line",
"ri-sun-line",
"ri-superscript-2",
"ri-superscript",
"ri-surgical-mask-fill",
"ri-surgical-mask-line",
"ri-surround-sound-fill",
"ri-surround-sound-line",
"ri-survey-fill",
"ri-survey-line",
"ri-swap-box-fill",
"ri-swap-box-line",
"ri-swap-fill",
"ri-swap-line",
"ri-switch-fill",
"ri-switch-line",
"ri-sword-fill",
"ri-sword-line",
"ri-syringe-fill",
"ri-syringe-line",
"ri-t-box-fill",
"ri-t-box-line",
"ri-t-shirt-2-fill",
"ri-t-shirt-2-line",
"ri-t-shirt-air-fill",
"ri-t-shirt-air-line",
"ri-t-shirt-fill",
"ri-t-shirt-line",
"ri-table-2",
"ri-table-alt-fill",
"ri-table-alt-line",
"ri-table-fill",
"ri-table-line",
"ri-tablet-fill",
"ri-tablet-line",
"ri-takeaway-fill",
"ri-takeaway-line",
"ri-taobao-fill",
"ri-taobao-line",
"ri-tape-fill",
"ri-tape-line",
"ri-task-fill",
"ri-task-line",
"ri-taxi-fill",
"ri-taxi-line",
"ri-taxi-wifi-fill",
"ri-taxi-wifi-line",
"ri-team-fill",
"ri-team-line",
"ri-telegram-fill",
"ri-telegram-line",
"ri-temp-cold-fill",
"ri-temp-cold-line",
"ri-temp-hot-fill",
"ri-temp-hot-line",
"ri-terminal-box-fill",
"ri-terminal-box-line",
"ri-terminal-fill",
"ri-terminal-line",
"ri-terminal-window-fill",
"ri-terminal-window-line",
"ri-test-tube-fill",
"ri-test-tube-line",
"ri-text-direction-l",
"ri-text-direction-r",
"ri-text-spacing",
"ri-text-wrap",
"ri-text",
"ri-thermometer-fill",
"ri-thermometer-line",
"ri-thumb-down-fill",
"ri-thumb-down-line",
"ri-thumb-up-fill",
"ri-thumb-up-line",
"ri-thunderstorms-fill",
"ri-thunderstorms-line",
"ri-ticket-2-fill",
"ri-ticket-2-line",
"ri-ticket-fill",
"ri-ticket-line",
"ri-time-fill",
"ri-time-line",
"ri-timer-2-fill",
"ri-timer-2-line",
"ri-timer-fill",
"ri-timer-flash-fill",
"ri-timer-flash-line",
"ri-timer-line",
"ri-todo-fill",
"ri-todo-line",
"ri-toggle-fill",
"ri-toggle-line",
"ri-tools-fill",
"ri-tools-line",
"ri-tornado-fill",
"ri-tornado-line",
"ri-trademark-fill",
"ri-trademark-line",
"ri-traffic-light-fill",
"ri-traffic-light-line",
"ri-train-fill",
"ri-train-line",
"ri-train-wifi-fill",
"ri-train-wifi-line",
"ri-translate-2",
"ri-translate",
"ri-travesti-fill",
"ri-travesti-line",
"ri-treasure-map-fill",
"ri-treasure-map-line",
"ri-trello-fill",
"ri-trello-line",
"ri-trophy-fill",
"ri-trophy-line",
"ri-truck-fill",
"ri-truck-line",
"ri-tumblr-fill",
"ri-tumblr-line",
"ri-tv-2-fill",
"ri-tv-2-line",
"ri-tv-fill",
"ri-tv-line",
"ri-twitch-fill",
"ri-twitch-line",
"ri-twitter-fill",
"ri-twitter-line",
"ri-typhoon-fill",
"ri-typhoon-line",
"ri-u-disk-fill",
"ri-u-disk-line",
"ri-ubuntu-fill",
"ri-ubuntu-line",
"ri-umbrella-fill",
"ri-umbrella-line",
"ri-underline",
"ri-uninstall-fill",
"ri-uninstall-line",
"ri-unsplash-fill",
"ri-unsplash-line",
"ri-upload-2-fill",
"ri-upload-2-line",
"ri-upload-cloud-2-fill",
"ri-upload-cloud-2-line",
"ri-upload-cloud-fill",
"ri-upload-cloud-line",
"ri-upload-fill",
"ri-upload-line",
"ri-usb-fill",
"ri-usb-line",
"ri-user-2-fill",
"ri-user-2-line",
"ri-user-3-fill",
"ri-user-3-line",
"ri-user-4-fill",
"ri-user-4-line",
"ri-user-5-fill",
"ri-user-5-line",
"ri-user-6-fill",
"ri-user-6-line",
"ri-user-add-fill",
"ri-user-add-line",
"ri-user-fill",
"ri-user-follow-fill",
"ri-user-follow-line",
"ri-user-heart-fill",
"ri-user-heart-line",
"ri-user-line",
"ri-user-location-fill",
"ri-user-location-line",
"ri-user-received-2-fill",
"ri-user-received-2-line",
"ri-user-received-fill",
"ri-user-received-line",
"ri-user-search-fill",
"ri-user-search-line",
"ri-user-settings-fill",
"ri-user-settings-line",
"ri-user-shared-2-fill",
"ri-user-shared-2-line",
"ri-user-shared-fill",
"ri-user-shared-line",
"ri-user-smile-fill",
"ri-user-smile-line",
"ri-user-star-fill",
"ri-user-star-line",
"ri-user-unfollow-fill",
"ri-user-unfollow-line",
"ri-user-voice-fill",
"ri-user-voice-line",
"ri-video-add-fill",
"ri-video-add-line",
"ri-video-chat-fill",
"ri-video-chat-line",
"ri-video-download-fill",
"ri-video-download-line",
"ri-video-fill",
"ri-video-line",
"ri-video-upload-fill",
"ri-video-upload-line",
"ri-vidicon-2-fill",
"ri-vidicon-2-line",
"ri-vidicon-fill",
"ri-vidicon-line",
"ri-vimeo-fill",
"ri-vimeo-line",
"ri-vip-crown-2-fill",
"ri-vip-crown-2-line",
"ri-vip-crown-fill",
"ri-vip-crown-line",
"ri-vip-diamond-fill",
"ri-vip-diamond-line",
"ri-vip-fill",
"ri-vip-line",
"ri-virus-fill",
"ri-virus-line",
"ri-visa-fill",
"ri-visa-line",
"ri-voice-recognition-fill",
"ri-voice-recognition-line",
"ri-voiceprint-fill",
"ri-voiceprint-line",
"ri-volume-down-fill",
"ri-volume-down-line",
"ri-volume-mute-fill",
"ri-volume-mute-line",
"ri-volume-off-vibrate-fill",
"ri-volume-off-vibrate-line",
"ri-volume-up-fill",
"ri-volume-up-line",
"ri-volume-vibrate-fill",
"ri-volume-vibrate-line",
"ri-vuejs-fill",
"ri-vuejs-line",
"ri-walk-fill",
"ri-walk-line",
"ri-wallet-2-fill",
"ri-wallet-2-line",
"ri-wallet-3-fill",
"ri-wallet-3-line",
"ri-wallet-fill",
"ri-wallet-line",
"ri-water-flash-fill",
"ri-water-flash-line",
"ri-webcam-fill",
"ri-webcam-line",
"ri-wechat-2-fill",
"ri-wechat-2-line",
"ri-wechat-fill",
"ri-wechat-line",
"ri-wechat-pay-fill",
"ri-wechat-pay-line",
"ri-weibo-fill",
"ri-weibo-line",
"ri-whatsapp-fill",
"ri-whatsapp-line",
"ri-wheelchair-fill",
"ri-wheelchair-line",
"ri-wifi-fill",
"ri-wifi-line",
"ri-wifi-off-fill",
"ri-wifi-off-line",
"ri-window-2-fill",
"ri-window-2-line",
"ri-window-fill",
"ri-window-line",
"ri-windows-fill",
"ri-windows-line",
"ri-windy-fill",
"ri-windy-line",
"ri-wireless-charging-fill",
"ri-wireless-charging-line",
"ri-women-fill",
"ri-women-line",
"ri-wubi-input",
"ri-xbox-fill",
"ri-xbox-line",
"ri-xing-fill",
"ri-xing-line",
"ri-youtube-fill",
"ri-youtube-line",
"ri-zcool-fill",
"ri-zcool-line",
"ri-zhihu-fill",
"ri-zhihu-line",
"ri-zoom-in-fill",
"ri-zoom-in-line",
"ri-zoom-out-fill",
"ri-zoom-out-line",
"ri-zzz-fill",
"ri-zzz-line",
"ri-arrow-down-double-fill",
"ri-arrow-down-double-line",
"ri-arrow-left-double-fill",
"ri-arrow-left-double-line",
"ri-arrow-right-double-fill",
"ri-arrow-right-double-line",
"ri-arrow-turn-back-fill",
"ri-arrow-turn-back-line",
"ri-arrow-turn-forward-fill",
"ri-arrow-turn-forward-line",
"ri-arrow-up-double-fill",
"ri-arrow-up-double-line",
"ri-bard-fill",
"ri-bard-line",
"ri-bootstrap-fill",
"ri-bootstrap-line",
"ri-box-1-fill",
"ri-box-1-line",
"ri-box-2-fill",
"ri-box-2-line",
"ri-box-3-fill",
"ri-box-3-line",
"ri-brain-fill",
"ri-brain-line",
"ri-candle-fill",
"ri-candle-line",
"ri-cash-fill",
"ri-cash-line",
"ri-contract-left-fill",
"ri-contract-left-line",
"ri-contract-left-right-fill",
"ri-contract-left-right-line",
"ri-contract-right-fill",
"ri-contract-right-line",
"ri-contract-up-down-fill",
"ri-contract-up-down-line",
"ri-copilot-fill",
"ri-copilot-line",
"ri-corner-down-left-fill",
"ri-corner-down-left-line",
"ri-corner-down-right-fill",
"ri-corner-down-right-line",
"ri-corner-left-down-fill",
"ri-corner-left-down-line",
"ri-corner-left-up-fill",
"ri-corner-left-up-line",
"ri-corner-right-down-fill",
"ri-corner-right-down-line",
"ri-corner-right-up-fill",
"ri-corner-right-up-line",
"ri-corner-up-left-double-fill",
"ri-corner-up-left-double-line",
"ri-corner-up-left-fill",
"ri-corner-up-left-line",
"ri-corner-up-right-double-fill",
"ri-corner-up-right-double-line",
"ri-corner-up-right-fill",
"ri-corner-up-right-line",
"ri-cross-fill",
"ri-cross-line",
"ri-edge-new-fill",
"ri-edge-new-line",
"ri-equal-fill",
"ri-equal-line",
"ri-expand-left-fill",
"ri-expand-left-line",
"ri-expand-left-right-fill",
"ri-expand-left-right-line",
"ri-expand-right-fill",
"ri-expand-right-line",
"ri-expand-up-down-fill",
"ri-expand-up-down-line",
"ri-flickr-fill",
"ri-flickr-line",
"ri-forward-10-fill",
"ri-forward-10-line",
"ri-forward-15-fill",
"ri-forward-15-line",
"ri-forward-30-fill",
"ri-forward-30-line",
"ri-forward-5-fill",
"ri-forward-5-line",
"ri-graduation-cap-fill",
"ri-graduation-cap-line",
"ri-home-office-fill",
"ri-home-office-line",
"ri-hourglass-2-fill",
"ri-hourglass-2-line",
"ri-hourglass-fill",
"ri-hourglass-line",
"ri-javascript-fill",
"ri-javascript-line",
"ri-loop-left-fill",
"ri-loop-left-line",
"ri-loop-right-fill",
"ri-loop-right-line",
"ri-memories-fill",
"ri-memories-line",
"ri-meta-fill",
"ri-meta-line",
"ri-microsoft-loop-fill",
"ri-microsoft-loop-line",
"ri-nft-fill",
"ri-nft-line",
"ri-notion-fill",
"ri-notion-line",
"ri-openai-fill",
"ri-openai-line",
"ri-overline",
"ri-p2p-fill",
"ri-p2p-line",
"ri-presentation-fill",
"ri-presentation-line",
"ri-replay-10-fill",
"ri-replay-10-line",
"ri-replay-15-fill",
"ri-replay-15-line",
"ri-replay-30-fill",
"ri-replay-30-line",
"ri-replay-5-fill",
"ri-replay-5-line",
"ri-school-fill",
"ri-school-line",
"ri-shining-2-fill",
"ri-shining-2-line",
"ri-shining-fill",
"ri-shining-line",
"ri-sketching",
"ri-skip-down-fill",
"ri-skip-down-line",
"ri-skip-left-fill",
"ri-skip-left-line",
"ri-skip-right-fill",
"ri-skip-right-line",
"ri-skip-up-fill",
"ri-skip-up-line",
"ri-slow-down-fill",
"ri-slow-down-line",
"ri-sparkling-2-fill",
"ri-sparkling-2-line",
"ri-sparkling-fill",
"ri-sparkling-line",
"ri-speak-fill",
"ri-speak-line",
"ri-speed-up-fill",
"ri-speed-up-line",
"ri-tiktok-fill",
"ri-tiktok-line",
"ri-token-swap-fill",
"ri-token-swap-line",
"ri-unpin-fill",
"ri-unpin-line",
"ri-wechat-channels-fill",
"ri-wechat-channels-line",
"ri-wordpress-fill",
"ri-wordpress-line",
"ri-blender-fill",
"ri-blender-line",
"ri-emoji-sticker-fill",
"ri-emoji-sticker-line",
"ri-git-close-pull-request-fill",
"ri-git-close-pull-request-line",
"ri-instance-fill",
"ri-instance-line",
"ri-megaphone-fill",
"ri-megaphone-line",
"ri-pass-expired-fill",
"ri-pass-expired-line",
"ri-pass-pending-fill",
"ri-pass-pending-line",
"ri-pass-valid-fill",
"ri-pass-valid-line",
"ri-ai-generate",
"ri-calendar-close-fill",
"ri-calendar-close-line",
"ri-draggable",
"ri-font-family",
"ri-font-mono",
"ri-font-sans-serif",
"ri-font-sans",
"ri-hard-drive-3-fill",
"ri-hard-drive-3-line",
"ri-kick-fill",
"ri-kick-line",
"ri-list-check-3",
"ri-list-indefinite",
"ri-list-ordered-2",
"ri-list-radio",
"ri-openbase-fill",
"ri-openbase-line",
"ri-planet-fill",
"ri-planet-line",
"ri-prohibited-fill",
"ri-prohibited-line",
"ri-quote-text",
"ri-seo-fill",
"ri-seo-line",
"ri-slash-commands",
"ri-archive-2-fill",
"ri-archive-2-line",
"ri-inbox-2-fill",
"ri-inbox-2-line",
"ri-shake-hands-fill",
"ri-shake-hands-line",
"ri-supabase-fill",
"ri-supabase-line",
"ri-water-percent-fill",
"ri-water-percent-line",
"ri-yuque-fill",
"ri-yuque-line",
"ri-crosshair-2-fill",
"ri-crosshair-2-line",
"ri-crosshair-fill",
"ri-crosshair-line",
"ri-file-close-fill",
"ri-file-close-line",
"ri-infinity-fill",
"ri-infinity-line",
"ri-rfid-fill",
"ri-rfid-line",
"ri-slash-commands-2",
"ri-user-forbid-fill",
"ri-user-forbid-line",
"ri-beer-fill",
"ri-beer-line",
"ri-circle-fill",
"ri-circle-line",
"ri-dropdown-list",
"ri-file-image-fill",
"ri-file-image-line",
"ri-file-pdf-2-fill",
"ri-file-pdf-2-line",
"ri-file-video-fill",
"ri-file-video-line",
"ri-folder-image-fill",
"ri-folder-image-line",
"ri-folder-video-fill",
"ri-folder-video-line",
"ri-hexagon-fill",
"ri-hexagon-line",
"ri-menu-search-fill",
"ri-menu-search-line",
"ri-octagon-fill",
"ri-octagon-line",
"ri-pentagon-fill",
"ri-pentagon-line",
"ri-rectangle-fill",
"ri-rectangle-line",
"ri-robot-2-fill",
"ri-robot-2-line",
"ri-shapes-fill",
"ri-shapes-line",
"ri-square-fill",
"ri-square-line",
"ri-tent-fill",
"ri-tent-line",
"ri-threads-fill",
"ri-threads-line",
"ri-tree-fill",
"ri-tree-line",
"ri-triangle-fill",
"ri-triangle-line",
"ri-twitter-x-fill",
"ri-twitter-x-line",
"ri-verified-badge-fill",
"ri-verified-badge-line",
"ri-armchair-fill",
"ri-armchair-line",
"ri-bnb-fill",
"ri-bnb-line",
"ri-bread-fill",
"ri-bread-line",
"ri-btc-fill",
"ri-btc-line",
"ri-calendar-schedule-fill",
"ri-calendar-schedule-line",
"ri-dice-1-fill",
"ri-dice-1-line",
"ri-dice-2-fill",
"ri-dice-2-line",
"ri-dice-3-fill",
"ri-dice-3-line",
"ri-dice-4-fill",
"ri-dice-4-line",
"ri-dice-5-fill",
"ri-dice-5-line",
"ri-dice-6-fill",
"ri-dice-6-line",
"ri-dice-fill",
"ri-dice-line",
"ri-drinks-fill",
"ri-drinks-line",
"ri-equalizer-2-fill",
"ri-equalizer-2-line",
"ri-equalizer-3-fill",
"ri-equalizer-3-line",
"ri-eth-fill",
"ri-eth-line",
"ri-flower-fill",
"ri-flower-line",
"ri-glasses-2-fill",
"ri-glasses-2-line",
"ri-glasses-fill",
"ri-glasses-line",
"ri-goggles-fill",
"ri-goggles-line",
"ri-image-circle-fill",
"ri-image-circle-line",
"ri-info-i",
"ri-money-rupee-circle-fill",
"ri-money-rupee-circle-line",
"ri-news-fill",
"ri-news-line",
"ri-robot-3-fill",
"ri-robot-3-line",
"ri-share-2-fill",
"ri-share-2-line",
"ri-sofa-fill",
"ri-sofa-line",
"ri-svelte-fill",
"ri-svelte-line",
"ri-vk-fill",
"ri-vk-line",
"ri-xrp-fill",
"ri-xrp-line",
"ri-xtz-fill",
"ri-xtz-line",
"ri-archive-stack-fill",
"ri-archive-stack-line",
"ri-bowl-fill",
"ri-bowl-line",
"ri-calendar-view",
"ri-carousel-view",
"ri-code-block",
"ri-color-filter-fill",
"ri-color-filter-line",
"ri-contacts-book-3-fill",
"ri-contacts-book-3-line",
"ri-contract-fill",
"ri-contract-line",
"ri-drinks-2-fill",
"ri-drinks-2-line",
"ri-export-fill",
"ri-export-line",
"ri-file-check-fill",
"ri-file-check-line",
"ri-focus-mode",
"ri-folder-6-fill",
"ri-folder-6-line",
"ri-folder-check-fill",
"ri-folder-check-line",
"ri-folder-close-fill",
"ri-folder-close-line",
"ri-folder-cloud-fill",
"ri-folder-cloud-line",
"ri-gallery-view-2",
"ri-gallery-view",
"ri-hand",
"ri-import-fill",
"ri-import-line",
"ri-information-2-fill",
"ri-information-2-line",
"ri-kanban-view-2",
"ri-kanban-view",
"ri-list-view",
"ri-lock-star-fill",
"ri-lock-star-line",
"ri-puzzle-2-fill",
"ri-puzzle-2-line",
"ri-puzzle-fill",
"ri-puzzle-line",
"ri-ram-2-fill",
"ri-ram-2-line",
"ri-ram-fill",
"ri-ram-line",
"ri-receipt-fill",
"ri-receipt-line",
"ri-shadow-fill",
"ri-shadow-line",
"ri-sidebar-fold-fill",
"ri-sidebar-fold-line",
"ri-sidebar-unfold-fill",
"ri-sidebar-unfold-line",
"ri-slideshow-view",
"ri-sort-alphabet-asc",
"ri-sort-alphabet-desc",
"ri-sort-number-asc",
"ri-sort-number-desc",
"ri-stacked-view",
"ri-sticky-note-add-fill",
"ri-sticky-note-add-line",
"ri-swap-2-fill",
"ri-swap-2-line",
"ri-swap-3-fill",
"ri-swap-3-line",
"ri-table-3",
"ri-table-view",
"ri-text-block",
"ri-text-snippet",
"ri-timeline-view",
"ri-blogger-fill",
"ri-blogger-line",
"ri-chat-thread-fill",
"ri-chat-thread-line",
"ri-discount-percent-fill",
"ri-discount-percent-line",
"ri-exchange-2-fill",
"ri-exchange-2-line",
"ri-git-fork-fill",
"ri-git-fork-line",
"ri-input-field",
"ri-progress-1-fill",
"ri-progress-1-line",
"ri-progress-2-fill",
"ri-progress-2-line",
"ri-progress-3-fill",
"ri-progress-3-line",
"ri-progress-4-fill",
"ri-progress-4-line",
"ri-progress-5-fill",
"ri-progress-5-line",
"ri-progress-6-fill",
"ri-progress-6-line",
"ri-progress-7-fill",
"ri-progress-7-line",
"ri-progress-8-fill",
"ri-progress-8-line",
"ri-remix-run-fill",
"ri-remix-run-line",
"ri-signpost-fill",
"ri-signpost-line",
"ri-time-zone-fill",
"ri-time-zone-line",
"ri-arrow-down-wide-fill",
"ri-arrow-down-wide-line",
"ri-arrow-left-wide-fill",
"ri-arrow-left-wide-line",
"ri-arrow-right-wide-fill",
"ri-arrow-right-wide-line",
"ri-arrow-up-wide-fill",
"ri-arrow-up-wide-line",
"ri-bluesky-fill",
"ri-bluesky-line",
"ri-expand-height-fill",
"ri-expand-height-line",
"ri-expand-width-fill",
"ri-expand-width-line",
"ri-forward-end-fill",
"ri-forward-end-line",
"ri-forward-end-mini-fill",
"ri-forward-end-mini-line",
"ri-friendica-fill",
"ri-friendica-line",
"ri-git-pr-draft-fill",
"ri-git-pr-draft-line",
"ri-play-reverse-fill",
"ri-play-reverse-line",
"ri-play-reverse-mini-fill",
"ri-play-reverse-mini-line",
"ri-rewind-start-fill",
"ri-rewind-start-line",
"ri-rewind-start-mini-fill",
"ri-rewind-start-mini-line",
"ri-scroll-to-bottom-fill",
"ri-scroll-to-bottom-line",
]