diff --git a/versions/0.2.2/.buildinfo b/versions/0.2.2/.buildinfo new file mode 100644 index 000000000..a346b281e --- /dev/null +++ b/versions/0.2.2/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: ffe18ab041802816329183a4ddfa3813 +tags: d77d1c0d9ca2f4c8421862c7c5a0d620 diff --git a/versions/0.2.2/.doctrees/camera_handles.doctree b/versions/0.2.2/.doctrees/camera_handles.doctree new file mode 100644 index 000000000..ba88eda4f Binary files /dev/null and b/versions/0.2.2/.doctrees/camera_handles.doctree differ diff --git a/versions/0.2.2/.doctrees/client_handles.doctree b/versions/0.2.2/.doctrees/client_handles.doctree new file mode 100644 index 000000000..e303e8202 Binary files /dev/null and b/versions/0.2.2/.doctrees/client_handles.doctree differ diff --git a/versions/0.2.2/.doctrees/conventions.doctree b/versions/0.2.2/.doctrees/conventions.doctree new file mode 100644 index 000000000..f3a8d41e3 Binary files /dev/null and b/versions/0.2.2/.doctrees/conventions.doctree differ diff --git a/versions/0.2.2/.doctrees/development.doctree b/versions/0.2.2/.doctrees/development.doctree new file mode 100644 index 000000000..7a0698e87 Binary files /dev/null and b/versions/0.2.2/.doctrees/development.doctree differ diff --git a/versions/0.2.2/.doctrees/environment.pickle b/versions/0.2.2/.doctrees/environment.pickle new file mode 100644 index 000000000..3d2d5c91e Binary files /dev/null and b/versions/0.2.2/.doctrees/environment.pickle differ diff --git a/versions/0.2.2/.doctrees/events.doctree b/versions/0.2.2/.doctrees/events.doctree new file mode 100644 index 000000000..e8705c496 Binary files /dev/null and b/versions/0.2.2/.doctrees/events.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/00_coordinate_frames.doctree b/versions/0.2.2/.doctrees/examples/00_coordinate_frames.doctree new file mode 100644 index 000000000..8022ac1aa Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/00_coordinate_frames.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/01_image.doctree b/versions/0.2.2/.doctrees/examples/01_image.doctree new file mode 100644 index 000000000..79ccbce73 Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/01_image.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/02_gui.doctree b/versions/0.2.2/.doctrees/examples/02_gui.doctree new file mode 100644 index 000000000..be3b437cb Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/02_gui.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/03_gui_callbacks.doctree b/versions/0.2.2/.doctrees/examples/03_gui_callbacks.doctree new file mode 100644 index 000000000..a0b946e72 Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/03_gui_callbacks.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/04_camera_poses.doctree b/versions/0.2.2/.doctrees/examples/04_camera_poses.doctree new file mode 100644 index 000000000..0c7b7891c Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/04_camera_poses.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/05_camera_commands.doctree b/versions/0.2.2/.doctrees/examples/05_camera_commands.doctree new file mode 100644 index 000000000..3ec9985a7 Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/05_camera_commands.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/06_mesh.doctree b/versions/0.2.2/.doctrees/examples/06_mesh.doctree new file mode 100644 index 000000000..2856b5acb Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/06_mesh.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/07_record3d_visualizer.doctree b/versions/0.2.2/.doctrees/examples/07_record3d_visualizer.doctree new file mode 100644 index 000000000..3906f1bbc Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/07_record3d_visualizer.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/08_smpl_visualizer.doctree b/versions/0.2.2/.doctrees/examples/08_smpl_visualizer.doctree new file mode 100644 index 000000000..d1acb7c31 Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/08_smpl_visualizer.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/09_urdf_visualizer.doctree b/versions/0.2.2/.doctrees/examples/09_urdf_visualizer.doctree new file mode 100644 index 000000000..8b83fa868 Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/09_urdf_visualizer.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/10_realsense.doctree b/versions/0.2.2/.doctrees/examples/10_realsense.doctree new file mode 100644 index 000000000..8f362cc49 Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/10_realsense.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/11_colmap_visualizer.doctree b/versions/0.2.2/.doctrees/examples/11_colmap_visualizer.doctree new file mode 100644 index 000000000..bc4d5807d Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/11_colmap_visualizer.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/12_click_meshes.doctree b/versions/0.2.2/.doctrees/examples/12_click_meshes.doctree new file mode 100644 index 000000000..be91b78c0 Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/12_click_meshes.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/13_theming.doctree b/versions/0.2.2/.doctrees/examples/13_theming.doctree new file mode 100644 index 000000000..f17ed00bf Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/13_theming.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/14_markdown.doctree b/versions/0.2.2/.doctrees/examples/14_markdown.doctree new file mode 100644 index 000000000..7e32d1b51 Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/14_markdown.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/15_gui_in_scene.doctree b/versions/0.2.2/.doctrees/examples/15_gui_in_scene.doctree new file mode 100644 index 000000000..a58504e46 Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/15_gui_in_scene.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/16_modal.doctree b/versions/0.2.2/.doctrees/examples/16_modal.doctree new file mode 100644 index 000000000..9c77bf911 Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/16_modal.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/17_background_composite.doctree b/versions/0.2.2/.doctrees/examples/17_background_composite.doctree new file mode 100644 index 000000000..b4683fa2c Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/17_background_composite.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/18_splines.doctree b/versions/0.2.2/.doctrees/examples/18_splines.doctree new file mode 100644 index 000000000..0be45bf82 Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/18_splines.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/19_get_renders.doctree b/versions/0.2.2/.doctrees/examples/19_get_renders.doctree new file mode 100644 index 000000000..40f0159cd Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/19_get_renders.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/20_scene_pointer.doctree b/versions/0.2.2/.doctrees/examples/20_scene_pointer.doctree new file mode 100644 index 000000000..81288eb4f Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/20_scene_pointer.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/21_set_up_direction.doctree b/versions/0.2.2/.doctrees/examples/21_set_up_direction.doctree new file mode 100644 index 000000000..1c0e64ad1 Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/21_set_up_direction.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/22_games.doctree b/versions/0.2.2/.doctrees/examples/22_games.doctree new file mode 100644 index 000000000..80728616c Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/22_games.doctree differ diff --git a/versions/0.2.2/.doctrees/examples/23_plotly.doctree b/versions/0.2.2/.doctrees/examples/23_plotly.doctree new file mode 100644 index 000000000..44a95951d Binary files /dev/null and b/versions/0.2.2/.doctrees/examples/23_plotly.doctree differ diff --git a/versions/0.2.2/.doctrees/extras.doctree b/versions/0.2.2/.doctrees/extras.doctree new file mode 100644 index 000000000..96b9558e8 Binary files /dev/null and b/versions/0.2.2/.doctrees/extras.doctree differ diff --git a/versions/0.2.2/.doctrees/gui_api.doctree b/versions/0.2.2/.doctrees/gui_api.doctree new file mode 100644 index 000000000..17e01dd53 Binary files /dev/null and b/versions/0.2.2/.doctrees/gui_api.doctree differ diff --git a/versions/0.2.2/.doctrees/gui_handles.doctree b/versions/0.2.2/.doctrees/gui_handles.doctree new file mode 100644 index 000000000..a19407d97 Binary files /dev/null and b/versions/0.2.2/.doctrees/gui_handles.doctree differ diff --git a/versions/0.2.2/.doctrees/icons.doctree b/versions/0.2.2/.doctrees/icons.doctree new file mode 100644 index 000000000..bd7c29fd8 Binary files /dev/null and b/versions/0.2.2/.doctrees/icons.doctree differ diff --git a/versions/0.2.2/.doctrees/index.doctree b/versions/0.2.2/.doctrees/index.doctree new file mode 100644 index 000000000..25eb041ee Binary files /dev/null and b/versions/0.2.2/.doctrees/index.doctree differ diff --git a/versions/0.2.2/.doctrees/infrastructure.doctree b/versions/0.2.2/.doctrees/infrastructure.doctree new file mode 100644 index 000000000..bbf6a7797 Binary files /dev/null and b/versions/0.2.2/.doctrees/infrastructure.doctree differ diff --git a/versions/0.2.2/.doctrees/scene_api.doctree b/versions/0.2.2/.doctrees/scene_api.doctree new file mode 100644 index 000000000..1f2a2988e Binary files /dev/null and b/versions/0.2.2/.doctrees/scene_api.doctree differ diff --git a/versions/0.2.2/.doctrees/scene_handles.doctree b/versions/0.2.2/.doctrees/scene_handles.doctree new file mode 100644 index 000000000..c7213cf6a Binary files /dev/null and b/versions/0.2.2/.doctrees/scene_handles.doctree differ diff --git a/versions/0.2.2/.doctrees/server.doctree b/versions/0.2.2/.doctrees/server.doctree new file mode 100644 index 000000000..5d9c0382f Binary files /dev/null and b/versions/0.2.2/.doctrees/server.doctree differ diff --git a/versions/0.2.2/.doctrees/transforms.doctree b/versions/0.2.2/.doctrees/transforms.doctree new file mode 100644 index 000000000..0ae47b744 Binary files /dev/null and b/versions/0.2.2/.doctrees/transforms.doctree differ diff --git a/versions/0.2.2/.nojekyll b/versions/0.2.2/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/versions/0.2.2/_modules/index.html b/versions/0.2.2/_modules/index.html new file mode 100644 index 000000000..a449acc0e --- /dev/null +++ b/versions/0.2.2/_modules/index.html @@ -0,0 +1,372 @@ + + + + + + + + + + + + Overview: module code - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+ +
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/_gui_api/index.html b/versions/0.2.2/_modules/viser/_gui_api/index.html new file mode 100644 index 000000000..06319c066 --- /dev/null +++ b/versions/0.2.2/_modules/viser/_gui_api/index.html @@ -0,0 +1,1923 @@ + + + + + + + + + + + + viser._gui_api - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser._gui_api

+from __future__ import annotations
+
+import colorsys
+import dataclasses
+import functools
+import threading
+import time
+from concurrent.futures import ThreadPoolExecutor
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Sequence, Tuple, TypeVar, Union, cast, overload
+
+import numpy as onp
+from typing_extensions import (
+    Literal,
+    LiteralString,
+    TypeAlias,
+    TypedDict,
+    get_args,
+    get_origin,
+    get_type_hints,
+)
+
+from viser import theme
+
+from . import _messages
+from ._gui_handles import (
+    GuiButtonGroupHandle,
+    GuiButtonHandle,
+    GuiContainerProtocol,
+    GuiDropdownHandle,
+    GuiEvent,
+    GuiFolderHandle,
+    GuiInputHandle,
+    GuiMarkdownHandle,
+    GuiModalHandle,
+    GuiPlotlyHandle,
+    GuiTabGroupHandle,
+    GuiUploadButtonHandle,
+    SupportsRemoveProtocol,
+    UploadedFile,
+    _GuiHandleState,
+    _GuiInputHandle,
+    _make_unique_id,
+)
+from ._icons import svg_from_icon
+from ._icons_enum import IconName
+from ._messages import FileTransferPartAck
+from ._scene_api import cast_vector
+
+if TYPE_CHECKING:
+    import plotly.graph_objects as go
+
+    from ._viser import ClientHandle, ViserServer
+    from .infra import ClientId
+
+
+IntOrFloat = TypeVar("IntOrFloat", int, float)
+TString = TypeVar("TString", bound=str)
+TLiteralString = TypeVar("TLiteralString", bound=LiteralString)
+T = TypeVar("T")
+LengthTenStrTuple: TypeAlias = Tuple[str, str, str, str, str, str, str, str, str, str]
+
+
+def _hex_from_hls(h: float, l: float, s: float) -> str:
+    """Converts HLS values in [0.0, 1.0] to a hex-formatted string, eg 0xffffff."""
+    return "#" + "".join(
+        [
+            int(min(255, max(0, channel * 255.0)) + 0.5).to_bytes(1, "little").hex()
+            for channel in colorsys.hls_to_rgb(h, l, s)
+        ]
+    )
+
+
+def _compute_step(x: float | None) -> float:  # type: ignore
+    """For number inputs: compute an increment size from some number.
+
+    Example inputs/outputs:
+        100 => 1
+        12 => 1
+        12.1 => 0.1
+        12.02 => 0.01
+        0.004 => 0.001
+    """
+    return 1 if x is None else 10 ** (-_compute_precision_digits(x))
+
+
+def _compute_precision_digits(x: float) -> int:
+    """For number inputs: compute digits of precision from some number.
+
+    Example inputs/outputs:
+        100 => 0
+        12 => 0
+        12.1 => 1
+        10.2 => 1
+        0.007 => 3
+    """
+    digits = 0
+    while x != round(x, ndigits=digits) and digits < 7:
+        digits += 1
+    return digits
+
+
+@dataclasses.dataclass
+class _RootGuiContainer:
+    _children: dict[str, SupportsRemoveProtocol]
+
+
+_global_order_counter = 0
+
+
+def _apply_default_order(order: float | None) -> float:
+    """Apply default ordering logic for GUI elements.
+
+    If `order` is set to a float, this function is a no-op and returns it back.
+    Otherwise, we increment and return the value of a global counter.
+    """
+    if order is not None:
+        return order
+
+    global _global_order_counter
+    _global_order_counter += 1
+    return _global_order_counter
+
+
+@functools.lru_cache(maxsize=None)
+def get_type_hints_cached(cls: type[Any]) -> dict[str, Any]:
+    return get_type_hints(cls)  # type: ignore
+
+
+def cast_value(tp, value):
+    """Cast a value to a type, or raise a TypeError if it cannot be cast."""
+    origin = get_origin(tp)
+
+    if (origin is tuple or tp is tuple) and isinstance(value, list):
+        return cast_value(tp, tuple(value))
+
+    if origin is Literal:
+        for val in get_args(tp):
+            try:
+                value_casted = cast_value(type(val), value)
+                if val == value_casted:
+                    return value_casted
+            except ValueError:
+                pass
+            except TypeError:
+                pass
+        raise TypeError(f"Value {value} is not in {get_args(tp)}")
+
+    if origin is Union:
+        for t in get_args(tp):
+            try:
+                return cast_value(t, value)
+            except ValueError:
+                pass
+            except TypeError:
+                pass
+        raise TypeError(f"Value {value} is not in {tp}")
+
+    if tp in {int, float, bool, str}:
+        return tp(value)
+
+    if dataclasses.is_dataclass(tp):
+        return tp(
+            **{k: cast_value(v, value[k]) for k, v in get_type_hints_cached(tp).items()}
+        )
+
+    if isinstance(value, tp):
+        return value
+
+    raise TypeError(f"Cannot cast value {value} to type {tp}")
+
+
+class _FileUploadState(TypedDict):
+    filename: str
+    mime_type: str
+    part_count: int
+    parts: dict[int, bytes]
+    total_bytes: int
+    transferred_bytes: int
+    lock: threading.Lock
+
+
+
+[docs] +class GuiApi: + """Interface for working with the 2D GUI in viser. + + Used by both our global server object, for sharing the same GUI elements + with all clients, and by invidividual client handles.""" + + _target_container_from_thread_id: dict[int, str] = {} + """ID of container to put GUI elements into.""" + + def __init__( + self, + owner: ViserServer | ClientHandle, # Who do I belong to? + thread_executor: ThreadPoolExecutor, + ) -> None: + from ._viser import ViserServer + + self._owner = owner + """Entity that owns this API.""" + self._thread_executor = thread_executor + + self._websock_interface = ( + owner._websock_server + if isinstance(owner, ViserServer) + else owner._websock_connection + ) + """Interface for sending and listening to messages.""" + + self._gui_input_handle_from_id: dict[str, _GuiInputHandle[Any]] = {} + self._container_handle_from_id: dict[str, GuiContainerProtocol] = { + "root": _RootGuiContainer({}) + } + self._current_file_upload_states: dict[str, _FileUploadState] = {} + + # Set to True when plotly.min.js has been sent to client. + self._setup_plotly_js: bool = False + + self._websock_interface.register_handler( + _messages.GuiUpdateMessage, self._handle_gui_updates + ) + self._websock_interface.register_handler( + _messages.FileTransferStart, self._handle_file_transfer_start + ) + self._websock_interface.register_handler( + _messages.FileTransferPart, + self._handle_file_transfer_part, + ) + + def _handle_gui_updates( + self, client_id: ClientId, message: _messages.GuiUpdateMessage + ) -> None: + """Callback for handling GUI messages.""" + handle = self._gui_input_handle_from_id.get(message.id, None) + if handle is None: + return + handle_state = handle._impl + + has_changed = False + updates_cast = {} + for prop_name, prop_value in message.updates.items(): + assert hasattr(handle_state, prop_name) + current_value = getattr(handle_state, prop_name) + + # Do some type casting. This is brittle, but necessary when we + # expect floats but the Javascript side gives us integers. + if prop_name == "value": + prop_value = cast_value(handle_state.typ, prop_value) + + # Update handle property. + if current_value != prop_value: + has_changed = True + setattr(handle_state, prop_name, prop_value) + + # Save value, which might have been cast. + updates_cast[prop_name] = prop_value + + # Only call update when value has actually changed. + if not handle_state.is_button and not has_changed: + return + + # GUI element has been updated! + handle_state.update_timestamp = time.time() + for cb in handle_state.update_cb: + from ._viser import ClientHandle, ViserServer + + # Get the handle of the client that triggered this event. + if isinstance(self._owner, ClientHandle): + client = self._owner + elif isinstance(self._owner, ViserServer): + client = self._owner.get_clients()[client_id] + else: + assert False + + cb(GuiEvent(client, client_id, handle)) + + if handle_state.sync_cb is not None: + handle_state.sync_cb(client_id, updates_cast) + + def _handle_file_transfer_start( + self, client_id: ClientId, message: _messages.FileTransferStart + ) -> None: + if message.source_component_id not in self._gui_input_handle_from_id: + return + self._current_file_upload_states[message.transfer_uuid] = { + "filename": message.filename, + "mime_type": message.mime_type, + "part_count": message.part_count, + "parts": {}, + "total_bytes": message.size_bytes, + "transferred_bytes": 0, + "lock": threading.Lock(), + } + + def _handle_file_transfer_part( + self, client_id: ClientId, message: _messages.FileTransferPart + ) -> None: + if message.transfer_uuid not in self._current_file_upload_states: + return + assert message.source_component_id in self._gui_input_handle_from_id + + state = self._current_file_upload_states[message.transfer_uuid] + state["parts"][message.part] = message.content + total_bytes = state["total_bytes"] + + with state["lock"]: + state["transferred_bytes"] += len(message.content) + + # Send ack to the server. + self._websock_interface.queue_message( + FileTransferPartAck( + source_component_id=message.source_component_id, + transfer_uuid=message.transfer_uuid, + transferred_bytes=state["transferred_bytes"], + total_bytes=total_bytes, + ) + ) + + if state["transferred_bytes"] < total_bytes: + return + + # Finish the upload. + assert state["transferred_bytes"] == total_bytes + state = self._current_file_upload_states.pop(message.transfer_uuid) + + handle = self._gui_input_handle_from_id.get(message.source_component_id, None) + if handle is None: + return + + handle_state = handle._impl + + value = UploadedFile( + name=state["filename"], + content=b"".join(state["parts"][i] for i in range(state["part_count"])), + ) + + # Update state. + with self._owner.atomic(): + handle_state.value = value + handle_state.update_timestamp = time.time() + + # Trigger callbacks. + for cb in handle_state.update_cb: + from ._viser import ClientHandle, ViserServer + + # Get the handle of the client that triggered this event. + if isinstance(self._owner, ClientHandle): + client = self._owner + elif isinstance(self._owner, ViserServer): + client = self._owner.get_clients()[client_id] + else: + assert False + + cb(GuiEvent(client, client_id, handle)) + + def _get_container_id(self) -> str: + """Get container ID associated with the current thread.""" + return self._target_container_from_thread_id.get(threading.get_ident(), "root") + + def _set_container_id(self, container_id: str) -> None: + """Set container ID associated with the current thread.""" + self._target_container_from_thread_id[threading.get_ident()] = container_id + +
+[docs] + def set_panel_label(self, label: str | None) -> None: + """Set the main label that appears in the GUI panel. + + Args: + label: The new label. + """ + self._websock_interface.queue_message(_messages.SetGuiPanelLabelMessage(label))
+ + +
+[docs] + def configure_theme( + self, + *, + titlebar_content: theme.TitlebarConfig | None = None, + control_layout: Literal["floating", "collapsible", "fixed"] = "floating", + control_width: Literal["small", "medium", "large"] = "medium", + dark_mode: bool = False, + show_logo: bool = True, + show_share_button: bool = True, + brand_color: tuple[int, int, int] | None = None, + ) -> None: + """Configures the visual appearance of the viser front-end. + + Args: + titlebar_content: Optional configuration for the title bar. + control_layout: The layout of control elements, options are "floating", + "collapsible", or "fixed". + control_width: The width of control elements, options are "small", + "medium", or "large". + dark_mode: A boolean indicating if dark mode should be enabled. + show_logo: A boolean indicating if the logo should be displayed. + show_share_button: A boolean indicating if the share button should be displayed. + brand_color: An optional tuple of integers (RGB) representing the brand color. + """ + + colors_cast: LengthTenStrTuple | None = None + + if brand_color is not None: + assert len(brand_color) in (3, 10) + if len(brand_color) == 3: + assert all( + map(lambda val: isinstance(val, int), brand_color) + ), "All channels should be integers." + + # RGB => HLS. + h, l, s = colorsys.rgb_to_hls( + brand_color[0] / 255.0, + brand_color[1] / 255.0, + brand_color[2] / 255.0, + ) + + # Automatically generate a 10-color palette. + min_l = max(l - 0.08, 0.0) + max_l = min(0.8 + 0.5, 0.9) + l = max(min_l, min(max_l, l)) + + primary_index = 8 + ls = tuple( + onp.interp( + x=onp.arange(10), + xp=onp.array([0, primary_index, 9]), + fp=onp.array([max_l, l, min_l]), + ) + ) + colors_cast = cast( + LengthTenStrTuple, + tuple(_hex_from_hls(h, ls[i], s) for i in range(10)), + ) + + assert colors_cast is None or all( + [isinstance(val, str) and val.startswith("#") for val in colors_cast] + ), "All string colors should be in hexadecimal + prefixed with #, eg #ffffff." + + self._websock_interface.queue_message( + _messages.ThemeConfigurationMessage( + titlebar_content=titlebar_content, + control_layout=control_layout, + control_width=control_width, + dark_mode=dark_mode, + show_logo=show_logo, + show_share_button=show_share_button, + colors=colors_cast, + ), + )
+ + +
+[docs] + def add_folder( + self, + label: str, + order: float | None = None, + expand_by_default: bool = True, + visible: bool = True, + ) -> GuiFolderHandle: + """Add a folder, and return a handle that can be used to populate it. + + Args: + label: Label to display on the folder. + order: Optional ordering, smallest values will be displayed first. + expand_by_default: Open the folder by default. Set to False to collapse it by + default. + visible: Whether the component is visible. + + Returns: + A handle that can be used as a context to populate the folder. + """ + folder_container_id = _make_unique_id() + order = _apply_default_order(order) + self._websock_interface.queue_message( + _messages.GuiAddFolderMessage( + order=order, + id=folder_container_id, + label=label, + container_id=self._get_container_id(), + expand_by_default=expand_by_default, + visible=visible, + ) + ) + return GuiFolderHandle( + _gui_api=self, + _id=folder_container_id, + _parent_container_id=self._get_container_id(), + _order=order, + )
+ + +
+[docs] + def add_modal( + self, + title: str, + order: float | None = None, + ) -> GuiModalHandle: + """Show a modal window, which can be useful for popups and messages, then return + a handle that can be used to populate it. + + Args: + title: Title to display on the modal. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used as a context to populate the modal. + """ + modal_container_id = _make_unique_id() + order = _apply_default_order(order) + self._websock_interface.queue_message( + _messages.GuiModalMessage( + order=order, + id=modal_container_id, + title=title, + ) + ) + return GuiModalHandle( + _gui_api=self, + _id=modal_container_id, + )
+ + +
+[docs] + def add_tab_group( + self, + order: float | None = None, + visible: bool = True, + ) -> GuiTabGroupHandle: + """Add a tab group. + + Args: + order: Optional ordering, smallest values will be displayed first. + visible: Whether the component is visible. + + Returns: + A handle that can be used as a context to populate the tab group. + """ + tab_group_id = _make_unique_id() + order = _apply_default_order(order) + + self._websock_interface.queue_message( + _messages.GuiAddTabGroupMessage( + order=order, + id=tab_group_id, + container_id=self._get_container_id(), + tab_labels=(), + visible=visible, + tab_icons_html=(), + tab_container_ids=(), + ) + ) + return GuiTabGroupHandle( + _tab_group_id=tab_group_id, + _labels=[], + _icons_html=[], + _tabs=[], + _gui_api=self, + _parent_container_id=self._get_container_id(), + _order=order, + )
+ + +
+[docs] + def add_markdown( + self, + content: str, + image_root: Path | None = None, + order: float | None = None, + visible: bool = True, + ) -> GuiMarkdownHandle: + """Add markdown to the GUI. + + Args: + content: Markdown content to display. + image_root: Optional root directory to resolve relative image paths. + order: Optional ordering, smallest values will be displayed first. + visible: Whether the component is visible. + + Returns: + A handle that can be used to interact with the GUI element. + """ + handle = GuiMarkdownHandle( + _gui_api=self, + _id=_make_unique_id(), + _visible=visible, + _parent_container_id=self._get_container_id(), + _order=_apply_default_order(order), + _image_root=image_root, + _content=None, + ) + self._websock_interface.queue_message( + _messages.GuiAddMarkdownMessage( + order=handle._order, + id=handle._id, + markdown="", + container_id=handle._parent_container_id, + visible=visible, + ) + ) + + # Logic for processing markdown, handling images, etc is all in the + # `.content` setter, which should send a GuiUpdateMessage. + handle.content = content + return handle
+ + +
+[docs] + def add_plotly( + self, + figure: go.Figure, + aspect: float = 1.0, + order: float | None = None, + visible: bool = True, + ) -> GuiPlotlyHandle: + """Add a Plotly figure to the GUI. Requires the `plotly` package to be + installed. + + Args: + figure: Plotly figure to display. + aspect: Aspect ratio of the plot in the control panel (width/height). + order: Optional ordering, smallest values will be displayed first. + visible: Whether the component is visible. + + Returns: + A handle that can be used to interact with the GUI element. + """ + handle = GuiPlotlyHandle( + _gui_api=self, + _id=_make_unique_id(), + _visible=visible, + _parent_container_id=self._get_container_id(), + _order=_apply_default_order(order), + _figure=None, + _aspect=None, + ) + + # If plotly.min.js hasn't been sent to the client yet, the client won't be able + # to render the plot. Send this large file now! (~3MB) + if not self._setup_plotly_js: + # Check if plotly is installed. + try: + import plotly + except ImportError: + raise ImportError( + "You must have the `plotly` package installed to use the Plotly GUI element." + ) + + # Check that plotly.min.js exists. + plotly_path = ( + Path(plotly.__file__).parent / "package_data" / "plotly.min.js" + ) + assert ( + plotly_path.exists() + ), f"Could not find plotly.min.js at {plotly_path}." + + # Send it over! + plotly_js = plotly_path.read_text(encoding="utf-8") + self._websock_interface.queue_message( + _messages.RunJavascriptMessage(source=plotly_js) + ) + + # Update the flag so we don't send it again. + self._setup_plotly_js = True + + # After plotly.min.js has been sent, we can send the plotly figure. + # Empty string for `plotly_json_str` is a signal to the client to render nothing. + self._websock_interface.queue_message( + _messages.GuiAddPlotlyMessage( + order=handle._order, + id=handle._id, + plotly_json_str="", + aspect=1.0, + container_id=handle._parent_container_id, + visible=visible, + ) + ) + + # Set the plotly handle properties. + handle.figure = figure + handle.aspect = aspect + + return handle
+ + +
+[docs] + def add_button( + self, + label: str, + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + color: Literal[ + "dark", + "gray", + "red", + "pink", + "grape", + "violet", + "indigo", + "blue", + "cyan", + "green", + "lime", + "yellow", + "orange", + "teal", + ] + | None = None, + icon: IconName | None = None, + order: float | None = None, + ) -> GuiButtonHandle: + """Add a button to the GUI. The value of this input is set to `True` every time + it is clicked; to detect clicks, we can manually set it back to `False`. + + Args: + label: Label to display on the button. + visible: Whether the button is visible. + disabled: Whether the button is disabled. + hint: Optional hint to display on hover. + color: Optional color to use for the button. + icon: Optional icon to display on the button. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + + # Re-wrap the GUI handle with a button interface. + id = _make_unique_id() + order = _apply_default_order(order) + return GuiButtonHandle( + self._create_gui_input( + value=False, + message=_messages.GuiAddButtonMessage( + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + value=False, + color=color, + icon_html=None if icon is None else svg_from_icon(icon), + disabled=disabled, + visible=visible, + ), + is_button=True, + )._impl + )
+ + +
+[docs] + def add_upload_button( + self, + label: str, + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + color: Literal[ + "dark", + "gray", + "red", + "pink", + "grape", + "violet", + "indigo", + "blue", + "cyan", + "green", + "lime", + "yellow", + "orange", + "teal", + ] + | None = None, + icon: IconName | None = None, + mime_type: str = "*/*", + order: float | None = None, + ) -> GuiUploadButtonHandle: + """Add a button to the GUI. The value of this input is set to `True` every time + it is clicked; to detect clicks, we can manually set it back to `False`. + + Args: + label: Label to display on the button. + visible: Whether the button is visible. + disabled: Whether the button is disabled. + hint: Optional hint to display on hover. + color: Optional color to use for the button. + icon: Optional icon to display on the button. + mime_type: Optional MIME type to filter the files that can be uploaded. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + + # Re-wrap the GUI handle with a button interface. + id = _make_unique_id() + order = _apply_default_order(order) + return GuiUploadButtonHandle( + self._create_gui_input( + value=UploadedFile("", b""), + message=_messages.GuiAddUploadButtonMessage( + value=None, + disabled=disabled, + visible=visible, + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + color=color, + mime_type=mime_type, + icon_html=None if icon is None else svg_from_icon(icon), + ), + is_button=True, + )._impl + )
+ + + # The TLiteralString overload tells pyright to resolve the value type to a Literal + # whenever possible. + # + # TString is helpful when the input types are generic (could be str, could be + # Literal). + @overload + def add_button_group( + self, + label: str, + options: Sequence[TLiteralString], + visible: bool = True, + disabled: bool = False, + hint: str | None = None, + order: float | None = None, + ) -> GuiButtonGroupHandle[TLiteralString]: ... + + @overload + def add_button_group( + self, + label: str, + options: Sequence[TString], + visible: bool = True, + disabled: bool = False, + hint: str | None = None, + order: float | None = None, + ) -> GuiButtonGroupHandle[TString]: ... + +
+[docs] + def add_button_group( + self, + label: str, + options: Sequence[TLiteralString] | Sequence[TString], + visible: bool = True, + disabled: bool = False, + hint: str | None = None, + order: float | None = None, + ) -> GuiButtonGroupHandle[Any]: # Return types are specified in overloads. + """Add a button group to the GUI. + + Args: + label: Label to display on the button group. + options: Sequence of options to display as buttons. + visible: Whether the button group is visible. + disabled: Whether the button group is disabled. + hint: Optional hint to display on hover. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + value = options[0] + id = _make_unique_id() + order = _apply_default_order(order) + return GuiButtonGroupHandle( + self._create_gui_input( + value, + message=_messages.GuiAddButtonGroupMessage( + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + value=value, + options=tuple(options), + disabled=disabled, + visible=visible, + ), + )._impl, + )
+ + +
+[docs] + def add_checkbox( + self, + label: str, + initial_value: bool, + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + order: float | None = None, + ) -> GuiInputHandle[bool]: + """Add a checkbox to the GUI. + + Args: + label: Label to display on the checkbox. + initial_value: Initial value of the checkbox. + disabled: Whether the checkbox is disabled. + visible: Whether the checkbox is visible. + hint: Optional hint to display on hover. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + value = initial_value + assert isinstance(value, bool) + id = _make_unique_id() + order = _apply_default_order(order) + return self._create_gui_input( + value, + message=_messages.GuiAddCheckboxMessage( + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + value=value, + disabled=disabled, + visible=visible, + ), + )
+ + +
+[docs] + def add_text( + self, + label: str, + initial_value: str, + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + order: float | None = None, + ) -> GuiInputHandle[str]: + """Add a text input to the GUI. + + Args: + label: Label to display on the text input. + initial_value: Initial value of the text input. + disabled: Whether the text input is disabled. + visible: Whether the text input is visible. + hint: Optional hint to display on hover. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + value = initial_value + assert isinstance(value, str) + id = _make_unique_id() + order = _apply_default_order(order) + return self._create_gui_input( + value, + message=_messages.GuiAddTextMessage( + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + value=value, + disabled=disabled, + visible=visible, + ), + )
+ + +
+[docs] + def add_number( + self, + label: str, + initial_value: IntOrFloat, + min: IntOrFloat | None = None, + max: IntOrFloat | None = None, + step: IntOrFloat | None = None, + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + order: float | None = None, + ) -> GuiInputHandle[IntOrFloat]: + """Add a number input to the GUI, with user-specifiable bound and precision parameters. + + Args: + label: Label to display on the number input. + initial_value: Initial value of the number input. + min: Optional minimum value of the number input. + max: Optional maximum value of the number input. + step: Optional step size of the number input. Computed automatically if not + specified. + disabled: Whether the number input is disabled. + visible: Whether the number input is visible. + hint: Optional hint to display on hover. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + value = initial_value + + assert isinstance(value, (int, float)) + + if step is None: + # It's ok that `step` is always a float, even if the value is an integer, + # because things all become `number` types after serialization. + step = float( # type: ignore + onp.min( + [ + _compute_step(value), + _compute_step(min), + _compute_step(max), + ] + ) + ) + + assert step is not None + + id = _make_unique_id() + order = _apply_default_order(order) + return self._create_gui_input( + value, + message=_messages.GuiAddNumberMessage( + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + value=value, + min=min, + max=max, + precision=_compute_precision_digits(step), + step=step, + disabled=disabled, + visible=visible, + ), + is_button=False, + )
+ + +
+[docs] + def add_vector2( + self, + label: str, + initial_value: tuple[float, float] | onp.ndarray, + min: tuple[float, float] | onp.ndarray | None = None, + max: tuple[float, float] | onp.ndarray | None = None, + step: float | None = None, + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + order: float | None = None, + ) -> GuiInputHandle[tuple[float, float]]: + """Add a length-2 vector input to the GUI. + + Args: + label: Label to display on the vector input. + initial_value: Initial value of the vector input. + min: Optional minimum value of the vector input. + max: Optional maximum value of the vector input. + step: Optional step size of the vector input. Computed automatically if not + disabled: Whether the vector input is disabled. + visible: Whether the vector input is visible. + hint: Optional hint to display on hover. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + value = initial_value + value = cast_vector(value, 2) + min = cast_vector(min, 2) if min is not None else None + max = cast_vector(max, 2) if max is not None else None + id = _make_unique_id() + order = _apply_default_order(order) + + if step is None: + possible_steps: list[float] = [] + possible_steps.extend([_compute_step(x) for x in value]) + if min is not None: + possible_steps.extend([_compute_step(x) for x in min]) + if max is not None: + possible_steps.extend([_compute_step(x) for x in max]) + step = float(onp.min(possible_steps)) + + return self._create_gui_input( + value, + message=_messages.GuiAddVector2Message( + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + value=value, + min=min, + max=max, + step=step, + precision=_compute_precision_digits(step), + disabled=disabled, + visible=visible, + ), + )
+ + +
+[docs] + def add_vector3( + self, + label: str, + initial_value: tuple[float, float, float] | onp.ndarray, + min: tuple[float, float, float] | onp.ndarray | None = None, + max: tuple[float, float, float] | onp.ndarray | None = None, + step: float | None = None, + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + order: float | None = None, + ) -> GuiInputHandle[tuple[float, float, float]]: + """Add a length-3 vector input to the GUI. + + Args: + label: Label to display on the vector input. + initial_value: Initial value of the vector input. + min: Optional minimum value of the vector input. + max: Optional maximum value of the vector input. + step: Optional step size of the vector input. Computed automatically if not + disabled: Whether the vector input is disabled. + visible: Whether the vector input is visible. + hint: Optional hint to display on hover. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + value = initial_value + value = cast_vector(value, 3) + min = cast_vector(min, 3) if min is not None else None + max = cast_vector(max, 3) if max is not None else None + id = _make_unique_id() + order = _apply_default_order(order) + + if step is None: + possible_steps: list[float] = [] + possible_steps.extend([_compute_step(x) for x in value]) + if min is not None: + possible_steps.extend([_compute_step(x) for x in min]) + if max is not None: + possible_steps.extend([_compute_step(x) for x in max]) + step = float(onp.min(possible_steps)) + + return self._create_gui_input( + value, + message=_messages.GuiAddVector3Message( + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + value=value, + min=min, + max=max, + step=step, + precision=_compute_precision_digits(step), + disabled=disabled, + visible=visible, + ), + )
+ + + # See add_dropdown for notes on overloads. + @overload + def add_dropdown( + self, + label: str, + options: Sequence[TLiteralString], + initial_value: TLiteralString | None = None, + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + order: float | None = None, + ) -> GuiDropdownHandle[TLiteralString]: ... + + @overload + def add_dropdown( + self, + label: str, + options: Sequence[TString], + initial_value: TString | None = None, + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + order: float | None = None, + ) -> GuiDropdownHandle[TString]: ... + +
+[docs] + def add_dropdown( + self, + label: str, + options: Sequence[TLiteralString] | Sequence[TString], + initial_value: TLiteralString | TString | None = None, + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + order: float | None = None, + ) -> GuiDropdownHandle[Any]: # Output type is specified in overloads. + """Add a dropdown to the GUI. + + Args: + label: Label to display on the dropdown. + options: Sequence of options to display in the dropdown. + initial_value: Initial value of the dropdown. + disabled: Whether the dropdown is disabled. + visible: Whether the dropdown is visible. + hint: Optional hint to display on hover. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + value = initial_value + if value is None: + value = options[0] + id = _make_unique_id() + order = _apply_default_order(order) + return GuiDropdownHandle( + self._create_gui_input( + value, + message=_messages.GuiAddDropdownMessage( + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + value=value, + options=tuple(options), + disabled=disabled, + visible=visible, + ), + )._impl, + _impl_options=tuple(options), + )
+ + +
+[docs] + def add_slider( + self, + label: str, + min: IntOrFloat, + max: IntOrFloat, + step: IntOrFloat, + initial_value: IntOrFloat, + marks: tuple[IntOrFloat | tuple[IntOrFloat, str], ...] | None = None, + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + order: float | None = None, + ) -> GuiInputHandle[IntOrFloat]: + """Add a slider to the GUI. Types of the min, max, step, and initial value should match. + + Args: + label: Label to display on the slider. + min: Minimum value of the slider. + max: Maximum value of the slider. + step: Step size of the slider. + initial_value: Initial value of the slider. + marks: tuple of marks to display below the slider. Each mark should + either be a numerical or a (number, label) tuple, where the + label is provided as a string. + disabled: Whether the slider is disabled. + visible: Whether the slider is visible. + hint: Optional hint to display on hover. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + value: IntOrFloat = initial_value + assert max >= min + if step > max - min: + step = max - min + assert max >= value >= min + + # GUI callbacks cast incoming values to match the type of the initial value. If + # the min, max, or step is a float, we should cast to a float. + # + # This should also match what the IntOrFloat TypeVar resolves to. + if type(value) is int and ( + type(min) is float or type(max) is float or type(step) is float + ): + value = float(value) # type: ignore + + # TODO: as of 6/5/2023, this assert will break something in nerfstudio. (at + # least LERF) + # + # assert type(min) == type(max) == type(step) == type(value) + + id = _make_unique_id() + order = _apply_default_order(order) + return self._create_gui_input( + value, + message=_messages.GuiAddSliderMessage( + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + min=min, + max=max, + step=step, + value=value, + precision=_compute_precision_digits(step), + visible=visible, + disabled=disabled, + marks=tuple( + {"value": float(x[0]), "label": x[1]} + if isinstance(x, tuple) + else {"value": float(x)} + for x in marks + ) + if marks is not None + else None, + ), + is_button=False, + )
+ + +
+[docs] + def add_multi_slider( + self, + label: str, + min: IntOrFloat, + max: IntOrFloat, + step: IntOrFloat, + initial_value: tuple[IntOrFloat, ...], + min_range: IntOrFloat | None = None, + fixed_endpoints: bool = False, + marks: tuple[IntOrFloat | tuple[IntOrFloat, str], ...] | None = None, + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + order: float | None = None, + ) -> GuiInputHandle[tuple[IntOrFloat, ...]]: + """Add a multi slider to the GUI. Types of the min, max, step, and initial value should match. + + Args: + label: Label to display on the slider. + min: Minimum value of the slider. + max: Maximum value of the slider. + step: Step size of the slider. + initial_value: Initial values of the slider. + min_range: Optional minimum difference between two values of the slider. + fixed_endpoints: Whether the endpoints of the slider are fixed. + marks: tuple of marks to display below the slider. Each mark should + either be a numerical or a (number, label) tuple, where the + label is provided as a string. + disabled: Whether the slider is disabled. + visible: Whether the slider is visible. + hint: Optional hint to display on hover. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + assert max >= min + if step > max - min: + step = max - min + assert all(max >= x >= min for x in initial_value) + + # GUI callbacks cast incoming values to match the type of the initial value. If + # any of the arguments are floats, we should always use a float value. + # + # This should also match what the IntOrFloat TypeVar resolves to. + if ( + type(min) is float + or type(max) is float + or type(step) is float + or type(min_range) is float + ): + initial_value = tuple(float(x) for x in initial_value) # type: ignore + + id = _make_unique_id() + order = _apply_default_order(order) + return self._create_gui_input( + value=initial_value, + message=_messages.GuiAddMultiSliderMessage( + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + min=min, + min_range=min_range, + max=max, + step=step, + value=initial_value, + visible=visible, + disabled=disabled, + fixed_endpoints=fixed_endpoints, + precision=_compute_precision_digits(step), + marks=tuple( + {"value": float(x[0]), "label": x[1]} + if isinstance(x, tuple) + else {"value": float(x)} + for x in marks + ) + if marks is not None + else None, + ), + is_button=False, + )
+ + +
+[docs] + def add_rgb( + self, + label: str, + initial_value: tuple[int, int, int], + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + order: float | None = None, + ) -> GuiInputHandle[tuple[int, int, int]]: + """Add an RGB picker to the GUI. + + Args: + label: Label to display on the RGB picker. + initial_value: Initial value of the RGB picker. + disabled: Whether the RGB picker is disabled. + visible: Whether the RGB picker is visible. + hint: Optional hint to display on hover. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + + value = initial_value + id = _make_unique_id() + order = _apply_default_order(order) + return self._create_gui_input( + value, + message=_messages.GuiAddRgbMessage( + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + value=value, + disabled=disabled, + visible=visible, + ), + )
+ + +
+[docs] + def add_rgba( + self, + label: str, + initial_value: tuple[int, int, int, int], + disabled: bool = False, + visible: bool = True, + hint: str | None = None, + order: float | None = None, + ) -> GuiInputHandle[tuple[int, int, int, int]]: + """Add an RGBA picker to the GUI. + + Args: + label: Label to display on the RGBA picker. + initial_value: Initial value of the RGBA picker. + disabled: Whether the RGBA picker is disabled. + visible: Whether the RGBA picker is visible. + hint: Optional hint to display on hover. + order: Optional ordering, smallest values will be displayed first. + + Returns: + A handle that can be used to interact with the GUI element. + """ + value = initial_value + id = _make_unique_id() + order = _apply_default_order(order) + return self._create_gui_input( + value, + message=_messages.GuiAddRgbaMessage( + order=order, + id=id, + label=label, + container_id=self._get_container_id(), + hint=hint, + value=value, + disabled=disabled, + visible=visible, + ), + )
+ + + def _create_gui_input( + self, + value: T, + message: _messages._GuiAddInputBase, + is_button: bool = False, + ) -> GuiInputHandle[T]: + """Private helper for adding a simple GUI element.""" + + # Send add GUI input message. + self._websock_interface.queue_message(message) + + # Construct handle. + handle_state = _GuiHandleState( + label=message.label, + message_type=type(message), + typ=type(value), + gui_api=self, + value=value, + update_timestamp=time.time(), + parent_container_id=self._get_container_id(), + update_cb=[], + is_button=is_button, + sync_cb=None, + disabled=message.disabled, + visible=message.visible, + id=message.id, + order=message.order, + hint=message.hint, + ) + + # For broadcasted GUI handles, we should synchronize all clients. + # This will be a no-op for client handles. + if not is_button: + + def sync_other_clients( + client_id: ClientId, updates: dict[str, Any] + ) -> None: + message = _messages.GuiUpdateMessage(handle_state.id, updates) + message.excluded_self_client = client_id + self._websock_interface.queue_message(message) + + handle_state.sync_cb = sync_other_clients + + handle = GuiInputHandle(handle_state) + + return handle
+ +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/_gui_handles/index.html b/versions/0.2.2/_modules/viser/_gui_handles/index.html new file mode 100644 index 000000000..8779ed362 --- /dev/null +++ b/versions/0.2.2/_modules/viser/_gui_handles/index.html @@ -0,0 +1,1122 @@ + + + + + + + + + + + + viser._gui_handles - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser._gui_handles

+from __future__ import annotations
+
+import dataclasses
+import re
+import time
+import urllib.parse
+import uuid
+import warnings
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Callable, Generic, Iterable, TypeVar
+
+import imageio.v3 as iio
+import numpy as onp
+from typing_extensions import Protocol
+
+from ._icons import svg_from_icon
+from ._icons_enum import IconName
+from ._messages import GuiCloseModalMessage, GuiRemoveMessage, GuiUpdateMessage, Message
+from ._scene_api import _encode_image_base64
+from .infra import ClientId
+
+if TYPE_CHECKING:
+    import plotly.graph_objects as go
+
+    from ._gui_api import GuiApi
+    from ._viser import ClientHandle
+
+
+T = TypeVar("T")
+TGuiHandle = TypeVar("TGuiHandle", bound="_GuiInputHandle")
+
+
+def _make_unique_id() -> str:
+    """Return a unique ID for referencing GUI elements."""
+    return str(uuid.uuid4())
+
+
+class GuiContainerProtocol(Protocol):
+    _children: dict[str, SupportsRemoveProtocol] = dataclasses.field(
+        default_factory=dict
+    )
+
+
+class SupportsRemoveProtocol(Protocol):
+    def remove(self) -> None: ...
+
+
+@dataclasses.dataclass
+class _GuiHandleState(Generic[T]):
+    """Internal API for GUI elements."""
+
+    label: str
+    typ: type[T]
+    gui_api: GuiApi
+    value: T
+    update_timestamp: float
+
+    parent_container_id: str
+    """Container that this GUI input was placed into."""
+
+    update_cb: list[Callable[[GuiEvent], None]]
+    """Registered functions to call when this input is updated."""
+
+    is_button: bool
+    """Indicates a button element, which requires special handling."""
+
+    sync_cb: Callable[[ClientId, dict[str, Any]], None] | None
+    """Callback for synchronizing inputs across clients."""
+
+    disabled: bool
+    visible: bool
+
+    order: float
+    id: str
+    hint: str | None
+
+    message_type: type[Message]
+
+
+@dataclasses.dataclass
+class _GuiInputHandle(Generic[T]):
+    # Let's shove private implementation details in here...
+    _impl: _GuiHandleState[T]
+
+    # Should we use @property for get_value / set_value, set_hidden, etc?
+    #
+    # Benefits:
+    #   @property is syntactically very nice.
+    #   `gui.value = ...` is really tempting!
+    #   Feels a bit more magical.
+    #
+    # Downsides:
+    #   Consistency: not everything that can be written can be read, and not everything
+    #   that can be read can be written. `get_`/`set_` makes this really clear.
+    #   Clarity: some things that we read (like client mappings) are copied before
+    #   they're returned. An attribute access obfuscates the overhead here.
+    #   Flexibility: getter/setter types should match. https://github.com/python/mypy/issues/3004
+    #   Feels a bit more magical.
+    #
+    # Is this worth the tradeoff?
+
+    @property
+    def order(self) -> float:
+        """Read-only order value, which dictates the position of the GUI element."""
+        return self._impl.order
+
+    @property
+    def value(self) -> T:
+        """Value of the GUI input. Synchronized automatically when assigned."""
+        return self._impl.value
+
+    @value.setter
+    def value(self, value: T | onp.ndarray) -> None:
+        if isinstance(value, onp.ndarray):
+            assert len(value.shape) <= 1, f"{value.shape} should be at most 1D!"
+            value = tuple(map(float, value))  # type: ignore
+
+        # Send to client, except for buttons.
+        if not self._impl.is_button:
+            self._impl.gui_api._websock_interface.queue_message(
+                GuiUpdateMessage(self._impl.id, {"value": value})
+            )
+
+        # Set internal state. We automatically convert numpy arrays to the expected
+        # internal type. (eg 1D arrays to tuples)
+        self._impl.value = type(self._impl.value)(value)  # type: ignore
+        self._impl.update_timestamp = time.time()
+
+        # Call update callbacks.
+        for cb in self._impl.update_cb:
+            # Pushing callbacks into separate threads helps prevent deadlocks when we
+            # have a lock in a callback. TODO: revisit other callbacks.
+            self._impl.gui_api._thread_executor.submit(
+                lambda: cb(
+                    GuiEvent(
+                        client_id=None,
+                        client=None,
+                        target=self,
+                    )
+                )
+            )
+
+    @property
+    def update_timestamp(self) -> float:
+        """Read-only timestamp when this input was last updated."""
+        return self._impl.update_timestamp
+
+    @property
+    def disabled(self) -> bool:
+        """Allow/disallow user interaction with the input. Synchronized automatically
+        when assigned."""
+        return self._impl.disabled
+
+    @disabled.setter
+    def disabled(self, disabled: bool) -> None:
+        if disabled == self.disabled:
+            return
+
+        self._impl.gui_api._websock_interface.queue_message(
+            GuiUpdateMessage(self._impl.id, {"disabled": disabled})
+        )
+        self._impl.disabled = disabled
+
+    @property
+    def visible(self) -> bool:
+        """Temporarily show or hide this GUI element from the visualizer. Synchronized
+        automatically when assigned."""
+        return self._impl.visible
+
+    @visible.setter
+    def visible(self, visible: bool) -> None:
+        if visible == self.visible:
+            return
+
+        self._impl.gui_api._websock_interface.queue_message(
+            GuiUpdateMessage(self._impl.id, {"visible": visible})
+        )
+        self._impl.visible = visible
+
+    def __post_init__(self) -> None:
+        """We need to register ourself after construction for callbacks to work."""
+        gui_api = self._impl.gui_api
+
+        # TODO: the current way we track GUI handles and children is very manual +
+        # error-prone. We should revist this design.
+        gui_api._gui_input_handle_from_id[self._impl.id] = self
+        parent = gui_api._container_handle_from_id[self._impl.parent_container_id]
+        parent._children[self._impl.id] = self
+
+    def remove(self) -> None:
+        """Permanently remove this GUI element from the visualizer."""
+        gui_api = self._impl.gui_api
+        gui_api._websock_interface.queue_message(GuiRemoveMessage(self._impl.id))
+        gui_api._gui_input_handle_from_id.pop(self._impl.id)
+        parent = gui_api._container_handle_from_id[self._impl.parent_container_id]
+        parent._children.pop(self._impl.id)
+
+
+StringType = TypeVar("StringType", bound=str)
+
+
+# GuiInputHandle[T] is used for all inputs except for buttons.
+#
+# We inherit from _GuiInputHandle to special-case buttons because the usage semantics
+# are slightly different: we have `on_click()` instead of `on_update()`.
+
+[docs] +@dataclasses.dataclass +class GuiInputHandle(_GuiInputHandle[T], Generic[T]): + """A handle is created for each GUI element that is added in `viser`. + Handles can be used to read and write state. + + When a GUI element is added via :attr:`ViserServer.gui`, state is + synchronized between all connected clients. When a GUI element is added via + :attr:`ClientHandle.gui`, state is local to a specific client. + """ + +
+[docs] + def on_update( + self: TGuiHandle, func: Callable[[GuiEvent[TGuiHandle]], None] + ) -> Callable[[GuiEvent[TGuiHandle]], None]: + """Attach a function to call when a GUI input is updated. Happens in a thread.""" + self._impl.update_cb.append(func) + return func
+
+ + + +
+[docs] +@dataclasses.dataclass(frozen=True) +class GuiEvent(Generic[TGuiHandle]): + """Information associated with a GUI event, such as an update or click. + + Passed as input to callback functions.""" + + client: ClientHandle | None + """Client that triggered this event.""" + client_id: int | None + """ID of client that triggered this event.""" + target: TGuiHandle + """GUI element that was affected."""
+ + + +
+[docs] +@dataclasses.dataclass +class GuiButtonHandle(_GuiInputHandle[bool]): + """Handle for a button input in our visualizer. + + Lets us detect clicks.""" + +
+[docs] + def on_click( + self: TGuiHandle, func: Callable[[GuiEvent[TGuiHandle]], None] + ) -> Callable[[GuiEvent[TGuiHandle]], None]: + """Attach a function to call when a button is pressed. Happens in a thread.""" + self._impl.update_cb.append(func) + return func
+
+ + + +@dataclasses.dataclass +class UploadedFile: + """Result of a file upload.""" + + name: str + """Name of the file.""" + content: bytes + """Contents of the file.""" + + +@dataclasses.dataclass +class GuiUploadButtonHandle(_GuiInputHandle[UploadedFile]): + """Handle for an upload file button in our visualizer. + + The `.value` attribute will be updated with the contents of uploaded files. + """ + + def on_upload( + self: TGuiHandle, func: Callable[[GuiEvent[TGuiHandle]], None] + ) -> Callable[[GuiEvent[TGuiHandle]], None]: + """Attach a function to call when a button is pressed. Happens in a thread.""" + self._impl.update_cb.append(func) + return func + + +
+[docs] +@dataclasses.dataclass +class GuiButtonGroupHandle(_GuiInputHandle[StringType], Generic[StringType]): + """Handle for a button group input in our visualizer. + + Lets us detect clicks.""" + +
+[docs] + def on_click( + self: TGuiHandle, func: Callable[[GuiEvent[TGuiHandle]], None] + ) -> Callable[[GuiEvent[TGuiHandle]], None]: + """Attach a function to call when a button is pressed. Happens in a thread.""" + self._impl.update_cb.append(func) + return func
+ + + @property + def disabled(self) -> bool: + """Button groups cannot be disabled.""" + return False + + @disabled.setter + def disabled(self, disabled: bool) -> None: + """Button groups cannot be disabled.""" + assert not disabled, "Button groups cannot be disabled."
+ + + +
+[docs] +@dataclasses.dataclass +class GuiDropdownHandle(GuiInputHandle[StringType], Generic[StringType]): + """Handle for a dropdown-style GUI input in our visualizer. + + Lets us get values, set values, and detect updates.""" + + _impl_options: tuple[StringType, ...] + + @property + def options(self) -> tuple[StringType, ...]: + """Options for our dropdown. Synchronized automatically when assigned. + + For projects that care about typing: the static type of `options` should be + consistent with the `StringType` associated with a handle. Literal types will be + inferred where possible when handles are instantiated; for the most flexibility, + we can declare handles as `GuiDropdownHandle[str]`. + """ + return self._impl_options + + @options.setter + def options(self, options: Iterable[StringType]) -> None: + self._impl_options = tuple(options) + + need_to_overwrite_value = self.value not in self._impl_options + if need_to_overwrite_value: + self._impl.gui_api._websock_interface.queue_message( + GuiUpdateMessage( + self._impl.id, + {"options": self._impl_options, "value": self._impl_options[0]}, + ) + ) + self._impl.value = self._impl_options[0] + else: + self._impl.gui_api._websock_interface.queue_message( + GuiUpdateMessage( + self._impl.id, + {"options": self._impl_options}, + ) + )
+ + + +
+[docs] +@dataclasses.dataclass(frozen=True) +class GuiTabGroupHandle: + """Handle for a tab group. Call :meth:`add_tab()` to add a tab.""" + + _tab_group_id: str + _labels: list[str] + _icons_html: list[str | None] + _tabs: list[GuiTabHandle] + _gui_api: GuiApi + _parent_container_id: str + _order: float + + @property + def order(self) -> float: + """Read-only order value, which dictates the position of the GUI element.""" + return self._order + +
+[docs] + def add_tab(self, label: str, icon: IconName | None = None) -> GuiTabHandle: + """Add a tab. Returns a handle we can use to add GUI elements to it.""" + + id = _make_unique_id() + + # We may want to make this thread-safe in the future. + out = GuiTabHandle(_parent=self, _id=id) + + self._labels.append(label) + self._icons_html.append(None if icon is None else svg_from_icon(icon)) + self._tabs.append(out) + + self._sync_with_client() + return out
+ + + def __post_init__(self) -> None: + parent = self._gui_api._container_handle_from_id[self._parent_container_id] + parent._children[self._tab_group_id] = self + +
+[docs] + def remove(self) -> None: + """Remove this tab group and all contained GUI elements.""" + for tab in tuple(self._tabs): + tab.remove() + gui_api = self._gui_api + gui_api._websock_interface.queue_message(GuiRemoveMessage(self._tab_group_id)) + parent = gui_api._container_handle_from_id[self._parent_container_id] + parent._children.pop(self._tab_group_id)
+ + + def _sync_with_client(self) -> None: + """Send messages for syncing tab state with the client.""" + self._gui_api._websock_interface.queue_message( + GuiUpdateMessage( + self._tab_group_id, + { + "tab_labels": tuple(self._labels), + "tab_icons_html": tuple(self._icons_html), + "tab_container_ids": tuple(tab._id for tab in self._tabs), + }, + ) + )
+ + + +
+[docs] +@dataclasses.dataclass +class GuiFolderHandle: + """Use as a context to place GUI elements into a folder.""" + + _gui_api: GuiApi + _id: str # Used as container ID for children. + _order: float + _parent_container_id: str # Container ID of parent. + _container_id_restore: str | None = None + _children: dict[str, SupportsRemoveProtocol] = dataclasses.field( + default_factory=dict + ) + + @property + def order(self) -> float: + """Read-only order value, which dictates the position of the GUI element.""" + return self._order + + def __enter__(self) -> GuiFolderHandle: + self._container_id_restore = self._gui_api._get_container_id() + self._gui_api._set_container_id(self._id) + return self + + def __exit__(self, *args) -> None: + del args + assert self._container_id_restore is not None + self._gui_api._set_container_id(self._container_id_restore) + self._container_id_restore = None + + def __post_init__(self) -> None: + self._gui_api._container_handle_from_id[self._id] = self + parent = self._gui_api._container_handle_from_id[self._parent_container_id] + parent._children[self._id] = self + +
+[docs] + def remove(self) -> None: + """Permanently remove this folder and all contained GUI elements from the + visualizer.""" + self._gui_api._websock_interface.queue_message(GuiRemoveMessage(self._id)) + for child in tuple(self._children.values()): + child.remove() + parent = self._gui_api._container_handle_from_id[self._parent_container_id] + parent._children.pop(self._id) + self._gui_api._container_handle_from_id.pop(self._id)
+
+ + + +@dataclasses.dataclass +class GuiModalHandle: + """Use as a context to place GUI elements into a modal.""" + + _gui_api: GuiApi + _id: str # Used as container ID of children. + _container_id_restore: str | None = None + _children: dict[str, SupportsRemoveProtocol] = dataclasses.field( + default_factory=dict + ) + + def __enter__(self) -> GuiModalHandle: + self._container_id_restore = self._gui_api._get_container_id() + self._gui_api._set_container_id(self._id) + return self + + def __exit__(self, *args) -> None: + del args + assert self._container_id_restore is not None + self._gui_api._set_container_id(self._container_id_restore) + self._container_id_restore = None + + def __post_init__(self) -> None: + self._gui_api._container_handle_from_id[self._id] = self + + def close(self) -> None: + """Close this modal and permananently remove all contained GUI elements.""" + self._gui_api._websock_interface.queue_message( + GuiCloseModalMessage(self._id), + ) + for child in tuple(self._children.values()): + child.remove() + self._gui_api._container_handle_from_id.pop(self._id) + + +
+[docs] +@dataclasses.dataclass +class GuiTabHandle: + """Use as a context to place GUI elements into a tab.""" + + _parent: GuiTabGroupHandle + _id: str # Used as container ID of children. + _container_id_restore: str | None = None + _children: dict[str, SupportsRemoveProtocol] = dataclasses.field( + default_factory=dict + ) + + def __enter__(self) -> GuiTabHandle: + self._container_id_restore = self._parent._gui_api._get_container_id() + self._parent._gui_api._set_container_id(self._id) + return self + + def __exit__(self, *args) -> None: + del args + assert self._container_id_restore is not None + self._parent._gui_api._set_container_id(self._container_id_restore) + self._container_id_restore = None + + def __post_init__(self) -> None: + self._parent._gui_api._container_handle_from_id[self._id] = self + +
+[docs] + def remove(self) -> None: + """Permanently remove this tab and all contained GUI elements from the + visualizer.""" + # We may want to make this thread-safe in the future. + container_index = -1 + for i, tab in enumerate(self._parent._tabs): + if tab is self: + container_index = i + break + assert container_index != -1, "Tab already removed!" + + self._parent._labels.pop(container_index) + self._parent._icons_html.pop(container_index) + self._parent._tabs.pop(container_index) + self._parent._sync_with_client() + for child in tuple(self._children.values()): + child.remove() + self._parent._gui_api._container_handle_from_id.pop(self._id)
+
+ + + +def _get_data_url(url: str, image_root: Path | None) -> str: + if not url.startswith("http") and not image_root: + warnings.warn( + ( + "No `image_root` provided. All relative paths will be scoped to viser's" + " installation path." + ), + stacklevel=2, + ) + if url.startswith("http") or url.startswith("data:"): + return url + if image_root is None: + image_root = Path(__file__).parent + try: + image = iio.imread(image_root / url) + data_uri = _encode_image_base64(image, "png") + url = urllib.parse.quote(f"{data_uri[1]}") + return f"data:{data_uri[0]};base64,{url}" + except (IOError, FileNotFoundError): + warnings.warn( + f"Failed to read image {url}, with image_root set to {image_root}.", + stacklevel=2, + ) + return url + + +def _parse_markdown(markdown: str, image_root: Path | None) -> str: + markdown = re.sub( + r"\!\[([^]]*)\]\(([^]]*)\)", + lambda match: ( + f"![{match.group(1)}]({_get_data_url(match.group(2), image_root)})" + ), + markdown, + ) + return markdown + + +
+[docs] +@dataclasses.dataclass +class GuiMarkdownHandle: + """Use to remove markdown.""" + + _gui_api: GuiApi + _id: str + _visible: bool + _parent_container_id: str # Parent. + _order: float + _image_root: Path | None + _content: str | None + + @property + def content(self) -> str: + """Current content of this markdown element. Synchronized automatically when assigned.""" + assert self._content is not None + return self._content + + @content.setter + def content(self, content: str) -> None: + self._content = content + self._gui_api._websock_interface.queue_message( + GuiUpdateMessage( + self._id, + {"markdown": _parse_markdown(content, self._image_root)}, + ) + ) + + @property + def order(self) -> float: + """Read-only order value, which dictates the position of the GUI element.""" + return self._order + + @property + def visible(self) -> bool: + """Temporarily show or hide this GUI element from the visualizer. Synchronized + automatically when assigned.""" + return self._visible + + @visible.setter + def visible(self, visible: bool) -> None: + if visible == self.visible: + return + + self._gui_api._websock_interface.queue_message( + GuiUpdateMessage(self._id, {"visible": visible}) + ) + self._visible = visible + +
+[docs] + def __post_init__(self) -> None: + """We need to register ourself after construction for callbacks to work.""" + parent = self._gui_api._container_handle_from_id[self._parent_container_id] + parent._children[self._id] = self
+ + +
+[docs] + def remove(self) -> None: + """Permanently remove this markdown from the visualizer.""" + self._gui_api._websock_interface.queue_message(GuiRemoveMessage(self._id)) + + parent = self._gui_api._container_handle_from_id[self._parent_container_id] + parent._children.pop(self._id)
+
+ + + +
+[docs] +@dataclasses.dataclass +class GuiPlotlyHandle: + """Use to update or remove markdown elements.""" + + _gui_api: GuiApi + _id: str + _visible: bool + _parent_container_id: str # Parent. + _order: float + _figure: go.Figure | None + _aspect: float | None + + @property + def figure(self) -> go.Figure: + """Current content of this markdown element. Synchronized automatically when assigned.""" + assert self._figure is not None + return self._figure + + @figure.setter + def figure(self, figure: go.Figure) -> None: + self._figure = figure + + json_str = figure.to_json() + assert isinstance(json_str, str) + + self._gui_api._websock_interface.queue_message( + GuiUpdateMessage( + self._id, + {"plotly_json_str": json_str}, + ) + ) + + @property + def aspect(self) -> float: + """Aspect ratio of the plotly figure, in the control panel.""" + assert self._aspect is not None + return self._aspect + + @aspect.setter + def aspect(self, aspect: float) -> None: + self._aspect = aspect + self._gui_api._websock_interface.queue_message( + GuiUpdateMessage( + self._id, + {"aspect": aspect}, + ) + ) + + @property + def order(self) -> float: + """Read-only order value, which dictates the position of the GUI element.""" + return self._order + + @property + def visible(self) -> bool: + """Temporarily show or hide this GUI element from the visualizer. Synchronized + automatically when assigned.""" + return self._visible + + @visible.setter + def visible(self, visible: bool) -> None: + if visible == self.visible: + return + + self._gui_api._websock_interface.queue_message( + GuiUpdateMessage(self._id, {"visible": visible}) + ) + self._visible = visible + +
+[docs] + def __post_init__(self) -> None: + """We need to register ourself after construction for callbacks to work.""" + parent = self._gui_api._container_handle_from_id[self._parent_container_id] + parent._children[self._id] = self
+ + +
+[docs] + def remove(self) -> None: + """Permanently remove this markdown from the visualizer.""" + self._gui_api._websock_interface.queue_message(GuiRemoveMessage(self._id)) + parent = self._gui_api._container_handle_from_id[self._parent_container_id] + parent._children.pop(self._id)
+
+ +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/_icons_enum/index.html b/versions/0.2.2/_modules/viser/_icons_enum/index.html new file mode 100644 index 000000000..9ef53b430 --- /dev/null +++ b/versions/0.2.2/_modules/viser/_icons_enum/index.html @@ -0,0 +1,4668 @@ + + + + + + + + + + + + viser._icons_enum - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser._icons_enum

+# Automatically generated by `_icons_generate_enum.py`
+# See https://tabler-icons.io/
+from typing import NewType
+
+IconName = NewType("IconName", str)
+"""Name of an icon. Should be generated via `viser.Icon.*`."""
+
+
+class _IconStringConverter(type):
+    def __getattr__(self, __name: str) -> IconName:
+        if not __name.startswith("_"):
+            return IconName(__name.lower().replace("_", "-"))
+        else:
+            raise AttributeError()
+
+
+
+[docs] +class Icon(metaclass=_IconStringConverter): + """'Enum' class for referencing Tabler icons. + + We don't subclass enum.Enum for performance reasons -- importing an enum with + thousands of names can result in import times in the hundreds of milliseconds. + + Attributes: + ICON_123 (IconName): The :code:`123` icon. + ICON_24_HOURS (IconName): The :code:`24-hours` icon. + ICON_2FA (IconName): The :code:`2fa` icon. + ICON_360 (IconName): The :code:`360` icon. + ICON_360_VIEW (IconName): The :code:`360-view` icon. + ICON_3D_CUBE_SPHERE (IconName): The :code:`3d-cube-sphere` icon. + ICON_3D_CUBE_SPHERE_OFF (IconName): The :code:`3d-cube-sphere-off` icon. + ICON_3D_ROTATE (IconName): The :code:`3d-rotate` icon. + A_B (IconName): The :code:`a-b` icon. + A_B_2 (IconName): The :code:`a-b-2` icon. + A_B_OFF (IconName): The :code:`a-b-off` icon. + ABACUS (IconName): The :code:`abacus` icon. + ABACUS_OFF (IconName): The :code:`abacus-off` icon. + ABC (IconName): The :code:`abc` icon. + ACCESS_POINT (IconName): The :code:`access-point` icon. + ACCESS_POINT_OFF (IconName): The :code:`access-point-off` icon. + ACCESSIBLE (IconName): The :code:`accessible` icon. + ACCESSIBLE_OFF (IconName): The :code:`accessible-off` icon. + ACCESSIBLE_OFF_FILLED (IconName): The :code:`accessible-off-filled` icon. + ACTIVITY (IconName): The :code:`activity` icon. + ACTIVITY_HEARTBEAT (IconName): The :code:`activity-heartbeat` icon. + AD (IconName): The :code:`ad` icon. + AD_2 (IconName): The :code:`ad-2` icon. + AD_CIRCLE (IconName): The :code:`ad-circle` icon. + AD_CIRCLE_FILLED (IconName): The :code:`ad-circle-filled` icon. + AD_CIRCLE_OFF (IconName): The :code:`ad-circle-off` icon. + AD_FILLED (IconName): The :code:`ad-filled` icon. + AD_OFF (IconName): The :code:`ad-off` icon. + ADDRESS_BOOK (IconName): The :code:`address-book` icon. + ADDRESS_BOOK_OFF (IconName): The :code:`address-book-off` icon. + ADJUSTMENTS (IconName): The :code:`adjustments` icon. + ADJUSTMENTS_ALT (IconName): The :code:`adjustments-alt` icon. + ADJUSTMENTS_BOLT (IconName): The :code:`adjustments-bolt` icon. + ADJUSTMENTS_CANCEL (IconName): The :code:`adjustments-cancel` icon. + ADJUSTMENTS_CHECK (IconName): The :code:`adjustments-check` icon. + ADJUSTMENTS_CODE (IconName): The :code:`adjustments-code` icon. + ADJUSTMENTS_COG (IconName): The :code:`adjustments-cog` icon. + ADJUSTMENTS_DOLLAR (IconName): The :code:`adjustments-dollar` icon. + ADJUSTMENTS_DOWN (IconName): The :code:`adjustments-down` icon. + ADJUSTMENTS_EXCLAMATION (IconName): The :code:`adjustments-exclamation` icon. + ADJUSTMENTS_FILLED (IconName): The :code:`adjustments-filled` icon. + ADJUSTMENTS_HEART (IconName): The :code:`adjustments-heart` icon. + ADJUSTMENTS_HORIZONTAL (IconName): The :code:`adjustments-horizontal` icon. + ADJUSTMENTS_MINUS (IconName): The :code:`adjustments-minus` icon. + ADJUSTMENTS_OFF (IconName): The :code:`adjustments-off` icon. + ADJUSTMENTS_PAUSE (IconName): The :code:`adjustments-pause` icon. + ADJUSTMENTS_PIN (IconName): The :code:`adjustments-pin` icon. + ADJUSTMENTS_PLUS (IconName): The :code:`adjustments-plus` icon. + ADJUSTMENTS_QUESTION (IconName): The :code:`adjustments-question` icon. + ADJUSTMENTS_SEARCH (IconName): The :code:`adjustments-search` icon. + ADJUSTMENTS_SHARE (IconName): The :code:`adjustments-share` icon. + ADJUSTMENTS_STAR (IconName): The :code:`adjustments-star` icon. + ADJUSTMENTS_UP (IconName): The :code:`adjustments-up` icon. + ADJUSTMENTS_X (IconName): The :code:`adjustments-x` icon. + AERIAL_LIFT (IconName): The :code:`aerial-lift` icon. + AFFILIATE (IconName): The :code:`affiliate` icon. + AFFILIATE_FILLED (IconName): The :code:`affiliate-filled` icon. + AIR_BALLOON (IconName): The :code:`air-balloon` icon. + AIR_CONDITIONING (IconName): The :code:`air-conditioning` icon. + AIR_CONDITIONING_DISABLED (IconName): The :code:`air-conditioning-disabled` icon. + ALARM (IconName): The :code:`alarm` icon. + ALARM_FILLED (IconName): The :code:`alarm-filled` icon. + ALARM_MINUS (IconName): The :code:`alarm-minus` icon. + ALARM_MINUS_FILLED (IconName): The :code:`alarm-minus-filled` icon. + ALARM_OFF (IconName): The :code:`alarm-off` icon. + ALARM_PLUS (IconName): The :code:`alarm-plus` icon. + ALARM_PLUS_FILLED (IconName): The :code:`alarm-plus-filled` icon. + ALARM_SNOOZE (IconName): The :code:`alarm-snooze` icon. + ALARM_SNOOZE_FILLED (IconName): The :code:`alarm-snooze-filled` icon. + ALBUM (IconName): The :code:`album` icon. + ALBUM_OFF (IconName): The :code:`album-off` icon. + ALERT_CIRCLE (IconName): The :code:`alert-circle` icon. + ALERT_CIRCLE_FILLED (IconName): The :code:`alert-circle-filled` icon. + ALERT_HEXAGON (IconName): The :code:`alert-hexagon` icon. + ALERT_HEXAGON_FILLED (IconName): The :code:`alert-hexagon-filled` icon. + ALERT_OCTAGON (IconName): The :code:`alert-octagon` icon. + ALERT_OCTAGON_FILLED (IconName): The :code:`alert-octagon-filled` icon. + ALERT_SMALL (IconName): The :code:`alert-small` icon. + ALERT_SQUARE (IconName): The :code:`alert-square` icon. + ALERT_SQUARE_FILLED (IconName): The :code:`alert-square-filled` icon. + ALERT_SQUARE_ROUNDED (IconName): The :code:`alert-square-rounded` icon. + ALERT_SQUARE_ROUNDED_FILLED (IconName): The :code:`alert-square-rounded-filled` icon. + ALERT_TRIANGLE (IconName): The :code:`alert-triangle` icon. + ALERT_TRIANGLE_FILLED (IconName): The :code:`alert-triangle-filled` icon. + ALIEN (IconName): The :code:`alien` icon. + ALIEN_FILLED (IconName): The :code:`alien-filled` icon. + ALIGN_BOX_BOTTOM_CENTER (IconName): The :code:`align-box-bottom-center` icon. + ALIGN_BOX_BOTTOM_CENTER_FILLED (IconName): The :code:`align-box-bottom-center-filled` icon. + ALIGN_BOX_BOTTOM_LEFT (IconName): The :code:`align-box-bottom-left` icon. + ALIGN_BOX_BOTTOM_LEFT_FILLED (IconName): The :code:`align-box-bottom-left-filled` icon. + ALIGN_BOX_BOTTOM_RIGHT (IconName): The :code:`align-box-bottom-right` icon. + ALIGN_BOX_BOTTOM_RIGHT_FILLED (IconName): The :code:`align-box-bottom-right-filled` icon. + ALIGN_BOX_CENTER_BOTTOM (IconName): The :code:`align-box-center-bottom` icon. + ALIGN_BOX_CENTER_MIDDLE (IconName): The :code:`align-box-center-middle` icon. + ALIGN_BOX_CENTER_MIDDLE_FILLED (IconName): The :code:`align-box-center-middle-filled` icon. + ALIGN_BOX_CENTER_STRETCH (IconName): The :code:`align-box-center-stretch` icon. + ALIGN_BOX_CENTER_TOP (IconName): The :code:`align-box-center-top` icon. + ALIGN_BOX_LEFT_BOTTOM (IconName): The :code:`align-box-left-bottom` icon. + ALIGN_BOX_LEFT_BOTTOM_FILLED (IconName): The :code:`align-box-left-bottom-filled` icon. + ALIGN_BOX_LEFT_MIDDLE (IconName): The :code:`align-box-left-middle` icon. + ALIGN_BOX_LEFT_MIDDLE_FILLED (IconName): The :code:`align-box-left-middle-filled` icon. + ALIGN_BOX_LEFT_STRETCH (IconName): The :code:`align-box-left-stretch` icon. + ALIGN_BOX_LEFT_TOP (IconName): The :code:`align-box-left-top` icon. + ALIGN_BOX_LEFT_TOP_FILLED (IconName): The :code:`align-box-left-top-filled` icon. + ALIGN_BOX_RIGHT_BOTTOM (IconName): The :code:`align-box-right-bottom` icon. + ALIGN_BOX_RIGHT_BOTTOM_FILLED (IconName): The :code:`align-box-right-bottom-filled` icon. + ALIGN_BOX_RIGHT_MIDDLE (IconName): The :code:`align-box-right-middle` icon. + ALIGN_BOX_RIGHT_MIDDLE_FILLED (IconName): The :code:`align-box-right-middle-filled` icon. + ALIGN_BOX_RIGHT_STRETCH (IconName): The :code:`align-box-right-stretch` icon. + ALIGN_BOX_RIGHT_TOP (IconName): The :code:`align-box-right-top` icon. + ALIGN_BOX_RIGHT_TOP_FILLED (IconName): The :code:`align-box-right-top-filled` icon. + ALIGN_BOX_TOP_CENTER (IconName): The :code:`align-box-top-center` icon. + ALIGN_BOX_TOP_CENTER_FILLED (IconName): The :code:`align-box-top-center-filled` icon. + ALIGN_BOX_TOP_LEFT (IconName): The :code:`align-box-top-left` icon. + ALIGN_BOX_TOP_LEFT_FILLED (IconName): The :code:`align-box-top-left-filled` icon. + ALIGN_BOX_TOP_RIGHT (IconName): The :code:`align-box-top-right` icon. + ALIGN_BOX_TOP_RIGHT_FILLED (IconName): The :code:`align-box-top-right-filled` icon. + ALIGN_CENTER (IconName): The :code:`align-center` icon. + ALIGN_JUSTIFIED (IconName): The :code:`align-justified` icon. + ALIGN_LEFT (IconName): The :code:`align-left` icon. + ALIGN_RIGHT (IconName): The :code:`align-right` icon. + ALPHA (IconName): The :code:`alpha` icon. + ALPHABET_CYRILLIC (IconName): The :code:`alphabet-cyrillic` icon. + ALPHABET_GREEK (IconName): The :code:`alphabet-greek` icon. + ALPHABET_LATIN (IconName): The :code:`alphabet-latin` icon. + AMBULANCE (IconName): The :code:`ambulance` icon. + AMPERSAND (IconName): The :code:`ampersand` icon. + ANALYZE (IconName): The :code:`analyze` icon. + ANALYZE_FILLED (IconName): The :code:`analyze-filled` icon. + ANALYZE_OFF (IconName): The :code:`analyze-off` icon. + ANCHOR (IconName): The :code:`anchor` icon. + ANCHOR_OFF (IconName): The :code:`anchor-off` icon. + ANGLE (IconName): The :code:`angle` icon. + ANKH (IconName): The :code:`ankh` icon. + ANTENNA (IconName): The :code:`antenna` icon. + ANTENNA_BARS_1 (IconName): The :code:`antenna-bars-1` icon. + ANTENNA_BARS_2 (IconName): The :code:`antenna-bars-2` icon. + ANTENNA_BARS_3 (IconName): The :code:`antenna-bars-3` icon. + ANTENNA_BARS_4 (IconName): The :code:`antenna-bars-4` icon. + ANTENNA_BARS_5 (IconName): The :code:`antenna-bars-5` icon. + ANTENNA_BARS_OFF (IconName): The :code:`antenna-bars-off` icon. + ANTENNA_OFF (IconName): The :code:`antenna-off` icon. + APERTURE (IconName): The :code:`aperture` icon. + APERTURE_OFF (IconName): The :code:`aperture-off` icon. + API (IconName): The :code:`api` icon. + API_APP (IconName): The :code:`api-app` icon. + API_APP_OFF (IconName): The :code:`api-app-off` icon. + API_OFF (IconName): The :code:`api-off` icon. + APP_WINDOW (IconName): The :code:`app-window` icon. + APP_WINDOW_FILLED (IconName): The :code:`app-window-filled` icon. + APPLE (IconName): The :code:`apple` icon. + APPS (IconName): The :code:`apps` icon. + APPS_FILLED (IconName): The :code:`apps-filled` icon. + APPS_OFF (IconName): The :code:`apps-off` icon. + ARCHIVE (IconName): The :code:`archive` icon. + ARCHIVE_FILLED (IconName): The :code:`archive-filled` icon. + ARCHIVE_OFF (IconName): The :code:`archive-off` icon. + ARMCHAIR (IconName): The :code:`armchair` icon. + ARMCHAIR_2 (IconName): The :code:`armchair-2` icon. + ARMCHAIR_2_OFF (IconName): The :code:`armchair-2-off` icon. + ARMCHAIR_OFF (IconName): The :code:`armchair-off` icon. + ARROW_AUTOFIT_CONTENT (IconName): The :code:`arrow-autofit-content` icon. + ARROW_AUTOFIT_CONTENT_FILLED (IconName): The :code:`arrow-autofit-content-filled` icon. + ARROW_AUTOFIT_DOWN (IconName): The :code:`arrow-autofit-down` icon. + ARROW_AUTOFIT_HEIGHT (IconName): The :code:`arrow-autofit-height` icon. + ARROW_AUTOFIT_LEFT (IconName): The :code:`arrow-autofit-left` icon. + ARROW_AUTOFIT_RIGHT (IconName): The :code:`arrow-autofit-right` icon. + ARROW_AUTOFIT_UP (IconName): The :code:`arrow-autofit-up` icon. + ARROW_AUTOFIT_WIDTH (IconName): The :code:`arrow-autofit-width` icon. + ARROW_BACK (IconName): The :code:`arrow-back` icon. + ARROW_BACK_UP (IconName): The :code:`arrow-back-up` icon. + ARROW_BACK_UP_DOUBLE (IconName): The :code:`arrow-back-up-double` icon. + ARROW_BADGE_DOWN (IconName): The :code:`arrow-badge-down` icon. + ARROW_BADGE_DOWN_FILLED (IconName): The :code:`arrow-badge-down-filled` icon. + ARROW_BADGE_LEFT (IconName): The :code:`arrow-badge-left` icon. + ARROW_BADGE_LEFT_FILLED (IconName): The :code:`arrow-badge-left-filled` icon. + ARROW_BADGE_RIGHT (IconName): The :code:`arrow-badge-right` icon. + ARROW_BADGE_RIGHT_FILLED (IconName): The :code:`arrow-badge-right-filled` icon. + ARROW_BADGE_UP (IconName): The :code:`arrow-badge-up` icon. + ARROW_BADGE_UP_FILLED (IconName): The :code:`arrow-badge-up-filled` icon. + ARROW_BAR_BOTH (IconName): The :code:`arrow-bar-both` icon. + ARROW_BAR_DOWN (IconName): The :code:`arrow-bar-down` icon. + ARROW_BAR_LEFT (IconName): The :code:`arrow-bar-left` icon. + ARROW_BAR_RIGHT (IconName): The :code:`arrow-bar-right` icon. + ARROW_BAR_TO_DOWN (IconName): The :code:`arrow-bar-to-down` icon. + ARROW_BAR_TO_LEFT (IconName): The :code:`arrow-bar-to-left` icon. + ARROW_BAR_TO_RIGHT (IconName): The :code:`arrow-bar-to-right` icon. + ARROW_BAR_TO_UP (IconName): The :code:`arrow-bar-to-up` icon. + ARROW_BAR_UP (IconName): The :code:`arrow-bar-up` icon. + ARROW_BEAR_LEFT (IconName): The :code:`arrow-bear-left` icon. + ARROW_BEAR_LEFT_2 (IconName): The :code:`arrow-bear-left-2` icon. + ARROW_BEAR_RIGHT (IconName): The :code:`arrow-bear-right` icon. + ARROW_BEAR_RIGHT_2 (IconName): The :code:`arrow-bear-right-2` icon. + ARROW_BIG_DOWN (IconName): The :code:`arrow-big-down` icon. + ARROW_BIG_DOWN_FILLED (IconName): The :code:`arrow-big-down-filled` icon. + ARROW_BIG_DOWN_LINE (IconName): The :code:`arrow-big-down-line` icon. + ARROW_BIG_DOWN_LINE_FILLED (IconName): The :code:`arrow-big-down-line-filled` icon. + ARROW_BIG_DOWN_LINES (IconName): The :code:`arrow-big-down-lines` icon. + ARROW_BIG_DOWN_LINES_FILLED (IconName): The :code:`arrow-big-down-lines-filled` icon. + ARROW_BIG_LEFT (IconName): The :code:`arrow-big-left` icon. + ARROW_BIG_LEFT_FILLED (IconName): The :code:`arrow-big-left-filled` icon. + ARROW_BIG_LEFT_LINE (IconName): The :code:`arrow-big-left-line` icon. + ARROW_BIG_LEFT_LINE_FILLED (IconName): The :code:`arrow-big-left-line-filled` icon. + ARROW_BIG_LEFT_LINES (IconName): The :code:`arrow-big-left-lines` icon. + ARROW_BIG_LEFT_LINES_FILLED (IconName): The :code:`arrow-big-left-lines-filled` icon. + ARROW_BIG_RIGHT (IconName): The :code:`arrow-big-right` icon. + ARROW_BIG_RIGHT_FILLED (IconName): The :code:`arrow-big-right-filled` icon. + ARROW_BIG_RIGHT_LINE (IconName): The :code:`arrow-big-right-line` icon. + ARROW_BIG_RIGHT_LINE_FILLED (IconName): The :code:`arrow-big-right-line-filled` icon. + ARROW_BIG_RIGHT_LINES (IconName): The :code:`arrow-big-right-lines` icon. + ARROW_BIG_RIGHT_LINES_FILLED (IconName): The :code:`arrow-big-right-lines-filled` icon. + ARROW_BIG_UP (IconName): The :code:`arrow-big-up` icon. + ARROW_BIG_UP_FILLED (IconName): The :code:`arrow-big-up-filled` icon. + ARROW_BIG_UP_LINE (IconName): The :code:`arrow-big-up-line` icon. + ARROW_BIG_UP_LINE_FILLED (IconName): The :code:`arrow-big-up-line-filled` icon. + ARROW_BIG_UP_LINES (IconName): The :code:`arrow-big-up-lines` icon. + ARROW_BIG_UP_LINES_FILLED (IconName): The :code:`arrow-big-up-lines-filled` icon. + ARROW_BOUNCE (IconName): The :code:`arrow-bounce` icon. + ARROW_CAPSULE (IconName): The :code:`arrow-capsule` icon. + ARROW_CURVE_LEFT (IconName): The :code:`arrow-curve-left` icon. + ARROW_CURVE_RIGHT (IconName): The :code:`arrow-curve-right` icon. + ARROW_DOWN (IconName): The :code:`arrow-down` icon. + ARROW_DOWN_BAR (IconName): The :code:`arrow-down-bar` icon. + ARROW_DOWN_CIRCLE (IconName): The :code:`arrow-down-circle` icon. + ARROW_DOWN_LEFT (IconName): The :code:`arrow-down-left` icon. + ARROW_DOWN_LEFT_CIRCLE (IconName): The :code:`arrow-down-left-circle` icon. + ARROW_DOWN_RHOMBUS (IconName): The :code:`arrow-down-rhombus` icon. + ARROW_DOWN_RIGHT (IconName): The :code:`arrow-down-right` icon. + ARROW_DOWN_RIGHT_CIRCLE (IconName): The :code:`arrow-down-right-circle` icon. + ARROW_DOWN_SQUARE (IconName): The :code:`arrow-down-square` icon. + ARROW_DOWN_TAIL (IconName): The :code:`arrow-down-tail` icon. + ARROW_ELBOW_LEFT (IconName): The :code:`arrow-elbow-left` icon. + ARROW_ELBOW_RIGHT (IconName): The :code:`arrow-elbow-right` icon. + ARROW_FORK (IconName): The :code:`arrow-fork` icon. + ARROW_FORWARD (IconName): The :code:`arrow-forward` icon. + ARROW_FORWARD_UP (IconName): The :code:`arrow-forward-up` icon. + ARROW_FORWARD_UP_DOUBLE (IconName): The :code:`arrow-forward-up-double` icon. + ARROW_GUIDE (IconName): The :code:`arrow-guide` icon. + ARROW_ITERATION (IconName): The :code:`arrow-iteration` icon. + ARROW_LEFT (IconName): The :code:`arrow-left` icon. + ARROW_LEFT_BAR (IconName): The :code:`arrow-left-bar` icon. + ARROW_LEFT_CIRCLE (IconName): The :code:`arrow-left-circle` icon. + ARROW_LEFT_RHOMBUS (IconName): The :code:`arrow-left-rhombus` icon. + ARROW_LEFT_RIGHT (IconName): The :code:`arrow-left-right` icon. + ARROW_LEFT_SQUARE (IconName): The :code:`arrow-left-square` icon. + ARROW_LEFT_TAIL (IconName): The :code:`arrow-left-tail` icon. + ARROW_LOOP_LEFT (IconName): The :code:`arrow-loop-left` icon. + ARROW_LOOP_LEFT_2 (IconName): The :code:`arrow-loop-left-2` icon. + ARROW_LOOP_RIGHT (IconName): The :code:`arrow-loop-right` icon. + ARROW_LOOP_RIGHT_2 (IconName): The :code:`arrow-loop-right-2` icon. + ARROW_MERGE (IconName): The :code:`arrow-merge` icon. + ARROW_MERGE_BOTH (IconName): The :code:`arrow-merge-both` icon. + ARROW_MERGE_LEFT (IconName): The :code:`arrow-merge-left` icon. + ARROW_MERGE_RIGHT (IconName): The :code:`arrow-merge-right` icon. + ARROW_MOVE_DOWN (IconName): The :code:`arrow-move-down` icon. + ARROW_MOVE_LEFT (IconName): The :code:`arrow-move-left` icon. + ARROW_MOVE_RIGHT (IconName): The :code:`arrow-move-right` icon. + ARROW_MOVE_UP (IconName): The :code:`arrow-move-up` icon. + ARROW_NARROW_DOWN (IconName): The :code:`arrow-narrow-down` icon. + ARROW_NARROW_LEFT (IconName): The :code:`arrow-narrow-left` icon. + ARROW_NARROW_RIGHT (IconName): The :code:`arrow-narrow-right` icon. + ARROW_NARROW_UP (IconName): The :code:`arrow-narrow-up` icon. + ARROW_RAMP_LEFT (IconName): The :code:`arrow-ramp-left` icon. + ARROW_RAMP_LEFT_2 (IconName): The :code:`arrow-ramp-left-2` icon. + ARROW_RAMP_LEFT_3 (IconName): The :code:`arrow-ramp-left-3` icon. + ARROW_RAMP_RIGHT (IconName): The :code:`arrow-ramp-right` icon. + ARROW_RAMP_RIGHT_2 (IconName): The :code:`arrow-ramp-right-2` icon. + ARROW_RAMP_RIGHT_3 (IconName): The :code:`arrow-ramp-right-3` icon. + ARROW_RIGHT (IconName): The :code:`arrow-right` icon. + ARROW_RIGHT_BAR (IconName): The :code:`arrow-right-bar` icon. + ARROW_RIGHT_CIRCLE (IconName): The :code:`arrow-right-circle` icon. + ARROW_RIGHT_RHOMBUS (IconName): The :code:`arrow-right-rhombus` icon. + ARROW_RIGHT_SQUARE (IconName): The :code:`arrow-right-square` icon. + ARROW_RIGHT_TAIL (IconName): The :code:`arrow-right-tail` icon. + ARROW_ROTARY_FIRST_LEFT (IconName): The :code:`arrow-rotary-first-left` icon. + ARROW_ROTARY_FIRST_RIGHT (IconName): The :code:`arrow-rotary-first-right` icon. + ARROW_ROTARY_LAST_LEFT (IconName): The :code:`arrow-rotary-last-left` icon. + ARROW_ROTARY_LAST_RIGHT (IconName): The :code:`arrow-rotary-last-right` icon. + ARROW_ROTARY_LEFT (IconName): The :code:`arrow-rotary-left` icon. + ARROW_ROTARY_RIGHT (IconName): The :code:`arrow-rotary-right` icon. + ARROW_ROTARY_STRAIGHT (IconName): The :code:`arrow-rotary-straight` icon. + ARROW_ROUNDABOUT_LEFT (IconName): The :code:`arrow-roundabout-left` icon. + ARROW_ROUNDABOUT_RIGHT (IconName): The :code:`arrow-roundabout-right` icon. + ARROW_SHARP_TURN_LEFT (IconName): The :code:`arrow-sharp-turn-left` icon. + ARROW_SHARP_TURN_RIGHT (IconName): The :code:`arrow-sharp-turn-right` icon. + ARROW_UP (IconName): The :code:`arrow-up` icon. + ARROW_UP_BAR (IconName): The :code:`arrow-up-bar` icon. + ARROW_UP_CIRCLE (IconName): The :code:`arrow-up-circle` icon. + ARROW_UP_LEFT (IconName): The :code:`arrow-up-left` icon. + ARROW_UP_LEFT_CIRCLE (IconName): The :code:`arrow-up-left-circle` icon. + ARROW_UP_RHOMBUS (IconName): The :code:`arrow-up-rhombus` icon. + ARROW_UP_RIGHT (IconName): The :code:`arrow-up-right` icon. + ARROW_UP_RIGHT_CIRCLE (IconName): The :code:`arrow-up-right-circle` icon. + ARROW_UP_SQUARE (IconName): The :code:`arrow-up-square` icon. + ARROW_UP_TAIL (IconName): The :code:`arrow-up-tail` icon. + ARROW_WAVE_LEFT_DOWN (IconName): The :code:`arrow-wave-left-down` icon. + ARROW_WAVE_LEFT_UP (IconName): The :code:`arrow-wave-left-up` icon. + ARROW_WAVE_RIGHT_DOWN (IconName): The :code:`arrow-wave-right-down` icon. + ARROW_WAVE_RIGHT_UP (IconName): The :code:`arrow-wave-right-up` icon. + ARROW_ZIG_ZAG (IconName): The :code:`arrow-zig-zag` icon. + ARROWS_CROSS (IconName): The :code:`arrows-cross` icon. + ARROWS_DIAGONAL (IconName): The :code:`arrows-diagonal` icon. + ARROWS_DIAGONAL_2 (IconName): The :code:`arrows-diagonal-2` icon. + ARROWS_DIAGONAL_MINIMIZE (IconName): The :code:`arrows-diagonal-minimize` icon. + ARROWS_DIAGONAL_MINIMIZE_2 (IconName): The :code:`arrows-diagonal-minimize-2` icon. + ARROWS_DIFF (IconName): The :code:`arrows-diff` icon. + ARROWS_DOUBLE_NE_SW (IconName): The :code:`arrows-double-ne-sw` icon. + ARROWS_DOUBLE_NW_SE (IconName): The :code:`arrows-double-nw-se` icon. + ARROWS_DOUBLE_SE_NW (IconName): The :code:`arrows-double-se-nw` icon. + ARROWS_DOUBLE_SW_NE (IconName): The :code:`arrows-double-sw-ne` icon. + ARROWS_DOWN (IconName): The :code:`arrows-down` icon. + ARROWS_DOWN_UP (IconName): The :code:`arrows-down-up` icon. + ARROWS_EXCHANGE (IconName): The :code:`arrows-exchange` icon. + ARROWS_EXCHANGE_2 (IconName): The :code:`arrows-exchange-2` icon. + ARROWS_HORIZONTAL (IconName): The :code:`arrows-horizontal` icon. + ARROWS_JOIN (IconName): The :code:`arrows-join` icon. + ARROWS_JOIN_2 (IconName): The :code:`arrows-join-2` icon. + ARROWS_LEFT (IconName): The :code:`arrows-left` icon. + ARROWS_LEFT_DOWN (IconName): The :code:`arrows-left-down` icon. + ARROWS_LEFT_RIGHT (IconName): The :code:`arrows-left-right` icon. + ARROWS_MAXIMIZE (IconName): The :code:`arrows-maximize` icon. + ARROWS_MINIMIZE (IconName): The :code:`arrows-minimize` icon. + ARROWS_MOVE (IconName): The :code:`arrows-move` icon. + ARROWS_MOVE_HORIZONTAL (IconName): The :code:`arrows-move-horizontal` icon. + ARROWS_MOVE_VERTICAL (IconName): The :code:`arrows-move-vertical` icon. + ARROWS_RANDOM (IconName): The :code:`arrows-random` icon. + ARROWS_RIGHT (IconName): The :code:`arrows-right` icon. + ARROWS_RIGHT_DOWN (IconName): The :code:`arrows-right-down` icon. + ARROWS_RIGHT_LEFT (IconName): The :code:`arrows-right-left` icon. + ARROWS_SHUFFLE (IconName): The :code:`arrows-shuffle` icon. + ARROWS_SHUFFLE_2 (IconName): The :code:`arrows-shuffle-2` icon. + ARROWS_SORT (IconName): The :code:`arrows-sort` icon. + ARROWS_SPLIT (IconName): The :code:`arrows-split` icon. + ARROWS_SPLIT_2 (IconName): The :code:`arrows-split-2` icon. + ARROWS_TRANSFER_DOWN (IconName): The :code:`arrows-transfer-down` icon. + ARROWS_TRANSFER_UP (IconName): The :code:`arrows-transfer-up` icon. + ARROWS_UP (IconName): The :code:`arrows-up` icon. + ARROWS_UP_DOWN (IconName): The :code:`arrows-up-down` icon. + ARROWS_UP_LEFT (IconName): The :code:`arrows-up-left` icon. + ARROWS_UP_RIGHT (IconName): The :code:`arrows-up-right` icon. + ARROWS_VERTICAL (IconName): The :code:`arrows-vertical` icon. + ARTBOARD (IconName): The :code:`artboard` icon. + ARTBOARD_FILLED (IconName): The :code:`artboard-filled` icon. + ARTBOARD_OFF (IconName): The :code:`artboard-off` icon. + ARTICLE (IconName): The :code:`article` icon. + ARTICLE_FILLED_FILLED (IconName): The :code:`article-filled-filled` icon. + ARTICLE_OFF (IconName): The :code:`article-off` icon. + ASPECT_RATIO (IconName): The :code:`aspect-ratio` icon. + ASPECT_RATIO_FILLED (IconName): The :code:`aspect-ratio-filled` icon. + ASPECT_RATIO_OFF (IconName): The :code:`aspect-ratio-off` icon. + ASSEMBLY (IconName): The :code:`assembly` icon. + ASSEMBLY_OFF (IconName): The :code:`assembly-off` icon. + ASSET (IconName): The :code:`asset` icon. + ASTERISK (IconName): The :code:`asterisk` icon. + ASTERISK_SIMPLE (IconName): The :code:`asterisk-simple` icon. + AT (IconName): The :code:`at` icon. + AT_OFF (IconName): The :code:`at-off` icon. + ATOM (IconName): The :code:`atom` icon. + ATOM_2 (IconName): The :code:`atom-2` icon. + ATOM_2_FILLED (IconName): The :code:`atom-2-filled` icon. + ATOM_OFF (IconName): The :code:`atom-off` icon. + AUGMENTED_REALITY (IconName): The :code:`augmented-reality` icon. + AUGMENTED_REALITY_2 (IconName): The :code:`augmented-reality-2` icon. + AUGMENTED_REALITY_OFF (IconName): The :code:`augmented-reality-off` icon. + AWARD (IconName): The :code:`award` icon. + AWARD_FILLED (IconName): The :code:`award-filled` icon. + AWARD_OFF (IconName): The :code:`award-off` icon. + AXE (IconName): The :code:`axe` icon. + AXIS_X (IconName): The :code:`axis-x` icon. + AXIS_Y (IconName): The :code:`axis-y` icon. + BABY_BOTTLE (IconName): The :code:`baby-bottle` icon. + BABY_CARRIAGE (IconName): The :code:`baby-carriage` icon. + BACKHOE (IconName): The :code:`backhoe` icon. + BACKPACK (IconName): The :code:`backpack` icon. + BACKPACK_OFF (IconName): The :code:`backpack-off` icon. + BACKSLASH (IconName): The :code:`backslash` icon. + BACKSPACE (IconName): The :code:`backspace` icon. + BACKSPACE_FILLED (IconName): The :code:`backspace-filled` icon. + BADGE (IconName): The :code:`badge` icon. + BADGE_3D (IconName): The :code:`badge-3d` icon. + BADGE_4K (IconName): The :code:`badge-4k` icon. + BADGE_8K (IconName): The :code:`badge-8k` icon. + BADGE_AD (IconName): The :code:`badge-ad` icon. + BADGE_AR (IconName): The :code:`badge-ar` icon. + BADGE_CC (IconName): The :code:`badge-cc` icon. + BADGE_FILLED (IconName): The :code:`badge-filled` icon. + BADGE_HD (IconName): The :code:`badge-hd` icon. + BADGE_OFF (IconName): The :code:`badge-off` icon. + BADGE_SD (IconName): The :code:`badge-sd` icon. + BADGE_TM (IconName): The :code:`badge-tm` icon. + BADGE_VO (IconName): The :code:`badge-vo` icon. + BADGE_VR (IconName): The :code:`badge-vr` icon. + BADGE_WC (IconName): The :code:`badge-wc` icon. + BADGES (IconName): The :code:`badges` icon. + BADGES_FILLED (IconName): The :code:`badges-filled` icon. + BADGES_OFF (IconName): The :code:`badges-off` icon. + BAGUETTE (IconName): The :code:`baguette` icon. + BALL_AMERICAN_FOOTBALL (IconName): The :code:`ball-american-football` icon. + BALL_AMERICAN_FOOTBALL_OFF (IconName): The :code:`ball-american-football-off` icon. + BALL_BASEBALL (IconName): The :code:`ball-baseball` icon. + BALL_BASKETBALL (IconName): The :code:`ball-basketball` icon. + BALL_BOWLING (IconName): The :code:`ball-bowling` icon. + BALL_FOOTBALL (IconName): The :code:`ball-football` icon. + BALL_FOOTBALL_OFF (IconName): The :code:`ball-football-off` icon. + BALL_TENNIS (IconName): The :code:`ball-tennis` icon. + BALL_VOLLEYBALL (IconName): The :code:`ball-volleyball` icon. + BALLOON (IconName): The :code:`balloon` icon. + BALLOON_FILLED (IconName): The :code:`balloon-filled` icon. + BALLOON_OFF (IconName): The :code:`balloon-off` icon. + BALLPEN (IconName): The :code:`ballpen` icon. + BALLPEN_FILLED (IconName): The :code:`ballpen-filled` icon. + BALLPEN_OFF (IconName): The :code:`ballpen-off` icon. + BAN (IconName): The :code:`ban` icon. + BANDAGE (IconName): The :code:`bandage` icon. + BANDAGE_FILLED (IconName): The :code:`bandage-filled` icon. + BANDAGE_OFF (IconName): The :code:`bandage-off` icon. + BARBELL (IconName): The :code:`barbell` icon. + BARBELL_OFF (IconName): The :code:`barbell-off` icon. + BARCODE (IconName): The :code:`barcode` icon. + BARCODE_OFF (IconName): The :code:`barcode-off` icon. + BARREL (IconName): The :code:`barrel` icon. + BARREL_OFF (IconName): The :code:`barrel-off` icon. + BARRIER_BLOCK (IconName): The :code:`barrier-block` icon. + BARRIER_BLOCK_OFF (IconName): The :code:`barrier-block-off` icon. + BASELINE (IconName): The :code:`baseline` icon. + BASELINE_DENSITY_LARGE (IconName): The :code:`baseline-density-large` icon. + BASELINE_DENSITY_MEDIUM (IconName): The :code:`baseline-density-medium` icon. + BASELINE_DENSITY_SMALL (IconName): The :code:`baseline-density-small` icon. + BASKET (IconName): The :code:`basket` icon. + BASKET_FILLED (IconName): The :code:`basket-filled` icon. + BASKET_OFF (IconName): The :code:`basket-off` icon. + BAT (IconName): The :code:`bat` icon. + BATH (IconName): The :code:`bath` icon. + BATH_FILLED (IconName): The :code:`bath-filled` icon. + BATH_OFF (IconName): The :code:`bath-off` icon. + BATTERY (IconName): The :code:`battery` icon. + BATTERY_1 (IconName): The :code:`battery-1` icon. + BATTERY_1_FILLED (IconName): The :code:`battery-1-filled` icon. + BATTERY_2 (IconName): The :code:`battery-2` icon. + BATTERY_2_FILLED (IconName): The :code:`battery-2-filled` icon. + BATTERY_3 (IconName): The :code:`battery-3` icon. + BATTERY_3_FILLED (IconName): The :code:`battery-3-filled` icon. + BATTERY_4 (IconName): The :code:`battery-4` icon. + BATTERY_4_FILLED (IconName): The :code:`battery-4-filled` icon. + BATTERY_AUTOMOTIVE (IconName): The :code:`battery-automotive` icon. + BATTERY_CHARGING (IconName): The :code:`battery-charging` icon. + BATTERY_CHARGING_2 (IconName): The :code:`battery-charging-2` icon. + BATTERY_ECO (IconName): The :code:`battery-eco` icon. + BATTERY_FILLED (IconName): The :code:`battery-filled` icon. + BATTERY_OFF (IconName): The :code:`battery-off` icon. + BEACH (IconName): The :code:`beach` icon. + BEACH_OFF (IconName): The :code:`beach-off` icon. + BED (IconName): The :code:`bed` icon. + BED_FILLED (IconName): The :code:`bed-filled` icon. + BED_OFF (IconName): The :code:`bed-off` icon. + BEER (IconName): The :code:`beer` icon. + BEER_FILLED (IconName): The :code:`beer-filled` icon. + BEER_OFF (IconName): The :code:`beer-off` icon. + BELL (IconName): The :code:`bell` icon. + BELL_BOLT (IconName): The :code:`bell-bolt` icon. + BELL_CANCEL (IconName): The :code:`bell-cancel` icon. + BELL_CHECK (IconName): The :code:`bell-check` icon. + BELL_CODE (IconName): The :code:`bell-code` icon. + BELL_COG (IconName): The :code:`bell-cog` icon. + BELL_DOLLAR (IconName): The :code:`bell-dollar` icon. + BELL_DOWN (IconName): The :code:`bell-down` icon. + BELL_EXCLAMATION (IconName): The :code:`bell-exclamation` icon. + BELL_FILLED (IconName): The :code:`bell-filled` icon. + BELL_HEART (IconName): The :code:`bell-heart` icon. + BELL_MINUS (IconName): The :code:`bell-minus` icon. + BELL_MINUS_FILLED (IconName): The :code:`bell-minus-filled` icon. + BELL_OFF (IconName): The :code:`bell-off` icon. + BELL_PAUSE (IconName): The :code:`bell-pause` icon. + BELL_PIN (IconName): The :code:`bell-pin` icon. + BELL_PLUS (IconName): The :code:`bell-plus` icon. + BELL_PLUS_FILLED (IconName): The :code:`bell-plus-filled` icon. + BELL_QUESTION (IconName): The :code:`bell-question` icon. + BELL_RINGING (IconName): The :code:`bell-ringing` icon. + BELL_RINGING_2 (IconName): The :code:`bell-ringing-2` icon. + BELL_RINGING_2_FILLED (IconName): The :code:`bell-ringing-2-filled` icon. + BELL_RINGING_FILLED (IconName): The :code:`bell-ringing-filled` icon. + BELL_SCHOOL (IconName): The :code:`bell-school` icon. + BELL_SEARCH (IconName): The :code:`bell-search` icon. + BELL_SHARE (IconName): The :code:`bell-share` icon. + BELL_STAR (IconName): The :code:`bell-star` icon. + BELL_UP (IconName): The :code:`bell-up` icon. + BELL_X (IconName): The :code:`bell-x` icon. + BELL_X_FILLED (IconName): The :code:`bell-x-filled` icon. + BELL_Z (IconName): The :code:`bell-z` icon. + BELL_Z_FILLED (IconName): The :code:`bell-z-filled` icon. + BETA (IconName): The :code:`beta` icon. + BIBLE (IconName): The :code:`bible` icon. + BIKE (IconName): The :code:`bike` icon. + BIKE_OFF (IconName): The :code:`bike-off` icon. + BINARY (IconName): The :code:`binary` icon. + BINARY_OFF (IconName): The :code:`binary-off` icon. + BINARY_TREE (IconName): The :code:`binary-tree` icon. + BINARY_TREE_2 (IconName): The :code:`binary-tree-2` icon. + BIOHAZARD (IconName): The :code:`biohazard` icon. + BIOHAZARD_OFF (IconName): The :code:`biohazard-off` icon. + BLADE (IconName): The :code:`blade` icon. + BLADE_FILLED (IconName): The :code:`blade-filled` icon. + BLEACH (IconName): The :code:`bleach` icon. + BLEACH_CHLORINE (IconName): The :code:`bleach-chlorine` icon. + BLEACH_NO_CHLORINE (IconName): The :code:`bleach-no-chlorine` icon. + BLEACH_OFF (IconName): The :code:`bleach-off` icon. + BLOCKQUOTE (IconName): The :code:`blockquote` icon. + BLUETOOTH (IconName): The :code:`bluetooth` icon. + BLUETOOTH_CONNECTED (IconName): The :code:`bluetooth-connected` icon. + BLUETOOTH_OFF (IconName): The :code:`bluetooth-off` icon. + BLUETOOTH_X (IconName): The :code:`bluetooth-x` icon. + BLUR (IconName): The :code:`blur` icon. + BLUR_OFF (IconName): The :code:`blur-off` icon. + BMP (IconName): The :code:`bmp` icon. + BOLD (IconName): The :code:`bold` icon. + BOLD_OFF (IconName): The :code:`bold-off` icon. + BOLT (IconName): The :code:`bolt` icon. + BOLT_OFF (IconName): The :code:`bolt-off` icon. + BOMB (IconName): The :code:`bomb` icon. + BOMB_FILLED (IconName): The :code:`bomb-filled` icon. + BONE (IconName): The :code:`bone` icon. + BONE_OFF (IconName): The :code:`bone-off` icon. + BONG (IconName): The :code:`bong` icon. + BONG_OFF (IconName): The :code:`bong-off` icon. + BOOK (IconName): The :code:`book` icon. + BOOK_2 (IconName): The :code:`book-2` icon. + BOOK_DOWNLOAD (IconName): The :code:`book-download` icon. + BOOK_FILLED (IconName): The :code:`book-filled` icon. + BOOK_OFF (IconName): The :code:`book-off` icon. + BOOK_UPLOAD (IconName): The :code:`book-upload` icon. + BOOKMARK (IconName): The :code:`bookmark` icon. + BOOKMARK_EDIT (IconName): The :code:`bookmark-edit` icon. + BOOKMARK_FILLED (IconName): The :code:`bookmark-filled` icon. + BOOKMARK_MINUS (IconName): The :code:`bookmark-minus` icon. + BOOKMARK_OFF (IconName): The :code:`bookmark-off` icon. + BOOKMARK_PLUS (IconName): The :code:`bookmark-plus` icon. + BOOKMARK_QUESTION (IconName): The :code:`bookmark-question` icon. + BOOKMARKS (IconName): The :code:`bookmarks` icon. + BOOKMARKS_OFF (IconName): The :code:`bookmarks-off` icon. + BOOKS (IconName): The :code:`books` icon. + BOOKS_OFF (IconName): The :code:`books-off` icon. + BORDER_ALL (IconName): The :code:`border-all` icon. + BORDER_BOTTOM (IconName): The :code:`border-bottom` icon. + BORDER_CORNERS (IconName): The :code:`border-corners` icon. + BORDER_HORIZONTAL (IconName): The :code:`border-horizontal` icon. + BORDER_INNER (IconName): The :code:`border-inner` icon. + BORDER_LEFT (IconName): The :code:`border-left` icon. + BORDER_NONE (IconName): The :code:`border-none` icon. + BORDER_OUTER (IconName): The :code:`border-outer` icon. + BORDER_RADIUS (IconName): The :code:`border-radius` icon. + BORDER_RIGHT (IconName): The :code:`border-right` icon. + BORDER_SIDES (IconName): The :code:`border-sides` icon. + BORDER_STYLE (IconName): The :code:`border-style` icon. + BORDER_STYLE_2 (IconName): The :code:`border-style-2` icon. + BORDER_TOP (IconName): The :code:`border-top` icon. + BORDER_VERTICAL (IconName): The :code:`border-vertical` icon. + BOTTLE (IconName): The :code:`bottle` icon. + BOTTLE_FILLED (IconName): The :code:`bottle-filled` icon. + BOTTLE_OFF (IconName): The :code:`bottle-off` icon. + BOUNCE_LEFT (IconName): The :code:`bounce-left` icon. + BOUNCE_RIGHT (IconName): The :code:`bounce-right` icon. + BOW (IconName): The :code:`bow` icon. + BOWL (IconName): The :code:`bowl` icon. + BOX (IconName): The :code:`box` icon. + BOX_ALIGN_BOTTOM (IconName): The :code:`box-align-bottom` icon. + BOX_ALIGN_BOTTOM_FILLED (IconName): The :code:`box-align-bottom-filled` icon. + BOX_ALIGN_BOTTOM_LEFT (IconName): The :code:`box-align-bottom-left` icon. + BOX_ALIGN_BOTTOM_LEFT_FILLED (IconName): The :code:`box-align-bottom-left-filled` icon. + BOX_ALIGN_BOTTOM_RIGHT (IconName): The :code:`box-align-bottom-right` icon. + BOX_ALIGN_BOTTOM_RIGHT_FILLED (IconName): The :code:`box-align-bottom-right-filled` icon. + BOX_ALIGN_LEFT (IconName): The :code:`box-align-left` icon. + BOX_ALIGN_LEFT_FILLED (IconName): The :code:`box-align-left-filled` icon. + BOX_ALIGN_RIGHT (IconName): The :code:`box-align-right` icon. + BOX_ALIGN_RIGHT_FILLED (IconName): The :code:`box-align-right-filled` icon. + BOX_ALIGN_TOP (IconName): The :code:`box-align-top` icon. + BOX_ALIGN_TOP_FILLED (IconName): The :code:`box-align-top-filled` icon. + BOX_ALIGN_TOP_LEFT (IconName): The :code:`box-align-top-left` icon. + BOX_ALIGN_TOP_LEFT_FILLED (IconName): The :code:`box-align-top-left-filled` icon. + BOX_ALIGN_TOP_RIGHT (IconName): The :code:`box-align-top-right` icon. + BOX_ALIGN_TOP_RIGHT_FILLED (IconName): The :code:`box-align-top-right-filled` icon. + BOX_MARGIN (IconName): The :code:`box-margin` icon. + BOX_MODEL (IconName): The :code:`box-model` icon. + BOX_MODEL_2 (IconName): The :code:`box-model-2` icon. + BOX_MODEL_2_OFF (IconName): The :code:`box-model-2-off` icon. + BOX_MODEL_OFF (IconName): The :code:`box-model-off` icon. + BOX_MULTIPLE (IconName): The :code:`box-multiple` icon. + BOX_MULTIPLE_0 (IconName): The :code:`box-multiple-0` icon. + BOX_MULTIPLE_1 (IconName): The :code:`box-multiple-1` icon. + BOX_MULTIPLE_2 (IconName): The :code:`box-multiple-2` icon. + BOX_MULTIPLE_3 (IconName): The :code:`box-multiple-3` icon. + BOX_MULTIPLE_4 (IconName): The :code:`box-multiple-4` icon. + BOX_MULTIPLE_5 (IconName): The :code:`box-multiple-5` icon. + BOX_MULTIPLE_6 (IconName): The :code:`box-multiple-6` icon. + BOX_MULTIPLE_7 (IconName): The :code:`box-multiple-7` icon. + BOX_MULTIPLE_8 (IconName): The :code:`box-multiple-8` icon. + BOX_MULTIPLE_9 (IconName): The :code:`box-multiple-9` icon. + BOX_OFF (IconName): The :code:`box-off` icon. + BOX_PADDING (IconName): The :code:`box-padding` icon. + BOX_SEAM (IconName): The :code:`box-seam` icon. + BRACES (IconName): The :code:`braces` icon. + BRACES_OFF (IconName): The :code:`braces-off` icon. + BRACKETS (IconName): The :code:`brackets` icon. + BRACKETS_CONTAIN (IconName): The :code:`brackets-contain` icon. + BRACKETS_CONTAIN_END (IconName): The :code:`brackets-contain-end` icon. + BRACKETS_CONTAIN_START (IconName): The :code:`brackets-contain-start` icon. + BRACKETS_OFF (IconName): The :code:`brackets-off` icon. + BRAILLE (IconName): The :code:`braille` icon. + BRAIN (IconName): The :code:`brain` icon. + BRAND_4CHAN (IconName): The :code:`brand-4chan` icon. + BRAND_ABSTRACT (IconName): The :code:`brand-abstract` icon. + BRAND_ADOBE (IconName): The :code:`brand-adobe` icon. + BRAND_ADONIS_JS (IconName): The :code:`brand-adonis-js` icon. + BRAND_AIRBNB (IconName): The :code:`brand-airbnb` icon. + BRAND_AIRTABLE (IconName): The :code:`brand-airtable` icon. + BRAND_ALGOLIA (IconName): The :code:`brand-algolia` icon. + BRAND_ALIPAY (IconName): The :code:`brand-alipay` icon. + BRAND_ALPINE_JS (IconName): The :code:`brand-alpine-js` icon. + BRAND_AMAZON (IconName): The :code:`brand-amazon` icon. + BRAND_AMD (IconName): The :code:`brand-amd` icon. + BRAND_AMIGO (IconName): The :code:`brand-amigo` icon. + BRAND_AMONG_US (IconName): The :code:`brand-among-us` icon. + BRAND_ANDROID (IconName): The :code:`brand-android` icon. + BRAND_ANGULAR (IconName): The :code:`brand-angular` icon. + BRAND_ANSIBLE (IconName): The :code:`brand-ansible` icon. + BRAND_AO3 (IconName): The :code:`brand-ao3` icon. + BRAND_APPGALLERY (IconName): The :code:`brand-appgallery` icon. + BRAND_APPLE (IconName): The :code:`brand-apple` icon. + BRAND_APPLE_ARCADE (IconName): The :code:`brand-apple-arcade` icon. + BRAND_APPLE_PODCAST (IconName): The :code:`brand-apple-podcast` icon. + BRAND_APPSTORE (IconName): The :code:`brand-appstore` icon. + BRAND_ASANA (IconName): The :code:`brand-asana` icon. + BRAND_AWS (IconName): The :code:`brand-aws` icon. + BRAND_AZURE (IconName): The :code:`brand-azure` icon. + BRAND_BACKBONE (IconName): The :code:`brand-backbone` icon. + BRAND_BADOO (IconName): The :code:`brand-badoo` icon. + BRAND_BAIDU (IconName): The :code:`brand-baidu` icon. + BRAND_BANDCAMP (IconName): The :code:`brand-bandcamp` icon. + BRAND_BANDLAB (IconName): The :code:`brand-bandlab` icon. + BRAND_BEATS (IconName): The :code:`brand-beats` icon. + BRAND_BEHANCE (IconName): The :code:`brand-behance` icon. + BRAND_BILIBILI (IconName): The :code:`brand-bilibili` icon. + BRAND_BINANCE (IconName): The :code:`brand-binance` icon. + BRAND_BING (IconName): The :code:`brand-bing` icon. + BRAND_BITBUCKET (IconName): The :code:`brand-bitbucket` icon. + BRAND_BLACKBERRY (IconName): The :code:`brand-blackberry` icon. + BRAND_BLENDER (IconName): The :code:`brand-blender` icon. + BRAND_BLOGGER (IconName): The :code:`brand-blogger` icon. + BRAND_BOOKING (IconName): The :code:`brand-booking` icon. + BRAND_BOOTSTRAP (IconName): The :code:`brand-bootstrap` icon. + BRAND_BULMA (IconName): The :code:`brand-bulma` icon. + BRAND_BUMBLE (IconName): The :code:`brand-bumble` icon. + BRAND_BUNPO (IconName): The :code:`brand-bunpo` icon. + BRAND_C_SHARP (IconName): The :code:`brand-c-sharp` icon. + BRAND_CAKE (IconName): The :code:`brand-cake` icon. + BRAND_CAKEPHP (IconName): The :code:`brand-cakephp` icon. + BRAND_CAMPAIGNMONITOR (IconName): The :code:`brand-campaignmonitor` icon. + BRAND_CARBON (IconName): The :code:`brand-carbon` icon. + BRAND_CASHAPP (IconName): The :code:`brand-cashapp` icon. + BRAND_CHROME (IconName): The :code:`brand-chrome` icon. + BRAND_CINEMA_4D (IconName): The :code:`brand-cinema-4d` icon. + BRAND_CITYMAPPER (IconName): The :code:`brand-citymapper` icon. + BRAND_CLOUDFLARE (IconName): The :code:`brand-cloudflare` icon. + BRAND_CODECOV (IconName): The :code:`brand-codecov` icon. + BRAND_CODEPEN (IconName): The :code:`brand-codepen` icon. + BRAND_CODESANDBOX (IconName): The :code:`brand-codesandbox` icon. + BRAND_COHOST (IconName): The :code:`brand-cohost` icon. + BRAND_COINBASE (IconName): The :code:`brand-coinbase` icon. + BRAND_COMEDY_CENTRAL (IconName): The :code:`brand-comedy-central` icon. + BRAND_COREOS (IconName): The :code:`brand-coreos` icon. + BRAND_COUCHDB (IconName): The :code:`brand-couchdb` icon. + BRAND_COUCHSURFING (IconName): The :code:`brand-couchsurfing` icon. + BRAND_CPP (IconName): The :code:`brand-cpp` icon. + BRAND_CRAFT (IconName): The :code:`brand-craft` icon. + BRAND_CRUNCHBASE (IconName): The :code:`brand-crunchbase` icon. + BRAND_CSS3 (IconName): The :code:`brand-css3` icon. + BRAND_CTEMPLAR (IconName): The :code:`brand-ctemplar` icon. + BRAND_CUCUMBER (IconName): The :code:`brand-cucumber` icon. + BRAND_CUPRA (IconName): The :code:`brand-cupra` icon. + BRAND_CYPRESS (IconName): The :code:`brand-cypress` icon. + BRAND_D3 (IconName): The :code:`brand-d3` icon. + BRAND_DAYS_COUNTER (IconName): The :code:`brand-days-counter` icon. + BRAND_DCOS (IconName): The :code:`brand-dcos` icon. + BRAND_DEBIAN (IconName): The :code:`brand-debian` icon. + BRAND_DEEZER (IconName): The :code:`brand-deezer` icon. + BRAND_DELIVEROO (IconName): The :code:`brand-deliveroo` icon. + BRAND_DENO (IconName): The :code:`brand-deno` icon. + BRAND_DENODO (IconName): The :code:`brand-denodo` icon. + BRAND_DEVIANTART (IconName): The :code:`brand-deviantart` icon. + BRAND_DIGG (IconName): The :code:`brand-digg` icon. + BRAND_DINGTALK (IconName): The :code:`brand-dingtalk` icon. + BRAND_DISCORD (IconName): The :code:`brand-discord` icon. + BRAND_DISCORD_FILLED (IconName): The :code:`brand-discord-filled` icon. + BRAND_DISNEY (IconName): The :code:`brand-disney` icon. + BRAND_DISQUS (IconName): The :code:`brand-disqus` icon. + BRAND_DJANGO (IconName): The :code:`brand-django` icon. + BRAND_DOCKER (IconName): The :code:`brand-docker` icon. + BRAND_DOCTRINE (IconName): The :code:`brand-doctrine` icon. + BRAND_DOLBY_DIGITAL (IconName): The :code:`brand-dolby-digital` icon. + BRAND_DOUBAN (IconName): The :code:`brand-douban` icon. + BRAND_DRIBBBLE (IconName): The :code:`brand-dribbble` icon. + BRAND_DRIBBBLE_FILLED (IconName): The :code:`brand-dribbble-filled` icon. + BRAND_DROPS (IconName): The :code:`brand-drops` icon. + BRAND_DRUPAL (IconName): The :code:`brand-drupal` icon. + BRAND_EDGE (IconName): The :code:`brand-edge` icon. + BRAND_ELASTIC (IconName): The :code:`brand-elastic` icon. + BRAND_ELECTRONIC_ARTS (IconName): The :code:`brand-electronic-arts` icon. + BRAND_EMBER (IconName): The :code:`brand-ember` icon. + BRAND_ENVATO (IconName): The :code:`brand-envato` icon. + BRAND_ETSY (IconName): The :code:`brand-etsy` icon. + BRAND_EVERNOTE (IconName): The :code:`brand-evernote` icon. + BRAND_FACEBOOK (IconName): The :code:`brand-facebook` icon. + BRAND_FACEBOOK_FILLED (IconName): The :code:`brand-facebook-filled` icon. + BRAND_FEEDLY (IconName): The :code:`brand-feedly` icon. + BRAND_FIGMA (IconName): The :code:`brand-figma` icon. + BRAND_FILEZILLA (IconName): The :code:`brand-filezilla` icon. + BRAND_FINDER (IconName): The :code:`brand-finder` icon. + BRAND_FIREBASE (IconName): The :code:`brand-firebase` icon. + BRAND_FIREFOX (IconName): The :code:`brand-firefox` icon. + BRAND_FIVERR (IconName): The :code:`brand-fiverr` icon. + BRAND_FLICKR (IconName): The :code:`brand-flickr` icon. + BRAND_FLIGHTRADAR24 (IconName): The :code:`brand-flightradar24` icon. + BRAND_FLIPBOARD (IconName): The :code:`brand-flipboard` icon. + BRAND_FLUTTER (IconName): The :code:`brand-flutter` icon. + BRAND_FORTNITE (IconName): The :code:`brand-fortnite` icon. + BRAND_FOURSQUARE (IconName): The :code:`brand-foursquare` icon. + BRAND_FRAMER (IconName): The :code:`brand-framer` icon. + BRAND_FRAMER_MOTION (IconName): The :code:`brand-framer-motion` icon. + BRAND_FUNIMATION (IconName): The :code:`brand-funimation` icon. + BRAND_GATSBY (IconName): The :code:`brand-gatsby` icon. + BRAND_GIT (IconName): The :code:`brand-git` icon. + BRAND_GITHUB (IconName): The :code:`brand-github` icon. + BRAND_GITHUB_COPILOT (IconName): The :code:`brand-github-copilot` icon. + BRAND_GITHUB_FILLED (IconName): The :code:`brand-github-filled` icon. + BRAND_GITLAB (IconName): The :code:`brand-gitlab` icon. + BRAND_GMAIL (IconName): The :code:`brand-gmail` icon. + BRAND_GOLANG (IconName): The :code:`brand-golang` icon. + BRAND_GOOGLE (IconName): The :code:`brand-google` icon. + BRAND_GOOGLE_ANALYTICS (IconName): The :code:`brand-google-analytics` icon. + BRAND_GOOGLE_BIG_QUERY (IconName): The :code:`brand-google-big-query` icon. + BRAND_GOOGLE_DRIVE (IconName): The :code:`brand-google-drive` icon. + BRAND_GOOGLE_FIT (IconName): The :code:`brand-google-fit` icon. + BRAND_GOOGLE_HOME (IconName): The :code:`brand-google-home` icon. + BRAND_GOOGLE_MAPS (IconName): The :code:`brand-google-maps` icon. + BRAND_GOOGLE_ONE (IconName): The :code:`brand-google-one` icon. + BRAND_GOOGLE_PHOTOS (IconName): The :code:`brand-google-photos` icon. + BRAND_GOOGLE_PLAY (IconName): The :code:`brand-google-play` icon. + BRAND_GOOGLE_PODCASTS (IconName): The :code:`brand-google-podcasts` icon. + BRAND_GRAMMARLY (IconName): The :code:`brand-grammarly` icon. + BRAND_GRAPHQL (IconName): The :code:`brand-graphql` icon. + BRAND_GRAVATAR (IconName): The :code:`brand-gravatar` icon. + BRAND_GRINDR (IconName): The :code:`brand-grindr` icon. + BRAND_GUARDIAN (IconName): The :code:`brand-guardian` icon. + BRAND_GUMROAD (IconName): The :code:`brand-gumroad` icon. + BRAND_HBO (IconName): The :code:`brand-hbo` icon. + BRAND_HEADLESSUI (IconName): The :code:`brand-headlessui` icon. + BRAND_HEXO (IconName): The :code:`brand-hexo` icon. + BRAND_HIPCHAT (IconName): The :code:`brand-hipchat` icon. + BRAND_HTML5 (IconName): The :code:`brand-html5` icon. + BRAND_INERTIA (IconName): The :code:`brand-inertia` icon. + BRAND_INSTAGRAM (IconName): The :code:`brand-instagram` icon. + BRAND_INTERCOM (IconName): The :code:`brand-intercom` icon. + BRAND_ITCH (IconName): The :code:`brand-itch` icon. + BRAND_JAVASCRIPT (IconName): The :code:`brand-javascript` icon. + BRAND_JUEJIN (IconName): The :code:`brand-juejin` icon. + BRAND_KBIN (IconName): The :code:`brand-kbin` icon. + BRAND_KICK (IconName): The :code:`brand-kick` icon. + BRAND_KICKSTARTER (IconName): The :code:`brand-kickstarter` icon. + BRAND_KOTLIN (IconName): The :code:`brand-kotlin` icon. + BRAND_LARAVEL (IconName): The :code:`brand-laravel` icon. + BRAND_LASTFM (IconName): The :code:`brand-lastfm` icon. + BRAND_LEETCODE (IconName): The :code:`brand-leetcode` icon. + BRAND_LETTERBOXD (IconName): The :code:`brand-letterboxd` icon. + BRAND_LINE (IconName): The :code:`brand-line` icon. + BRAND_LINKEDIN (IconName): The :code:`brand-linkedin` icon. + BRAND_LINKTREE (IconName): The :code:`brand-linktree` icon. + BRAND_LINQPAD (IconName): The :code:`brand-linqpad` icon. + BRAND_LOOM (IconName): The :code:`brand-loom` icon. + BRAND_MAILGUN (IconName): The :code:`brand-mailgun` icon. + BRAND_MANTINE (IconName): The :code:`brand-mantine` icon. + BRAND_MASTERCARD (IconName): The :code:`brand-mastercard` icon. + BRAND_MASTODON (IconName): The :code:`brand-mastodon` icon. + BRAND_MATRIX (IconName): The :code:`brand-matrix` icon. + BRAND_MCDONALDS (IconName): The :code:`brand-mcdonalds` icon. + BRAND_MEDIUM (IconName): The :code:`brand-medium` icon. + BRAND_MERCEDES (IconName): The :code:`brand-mercedes` icon. + BRAND_MESSENGER (IconName): The :code:`brand-messenger` icon. + BRAND_META (IconName): The :code:`brand-meta` icon. + BRAND_MICROSOFT_TEAMS (IconName): The :code:`brand-microsoft-teams` icon. + BRAND_MINECRAFT (IconName): The :code:`brand-minecraft` icon. + BRAND_MINIPROGRAM (IconName): The :code:`brand-miniprogram` icon. + BRAND_MIXPANEL (IconName): The :code:`brand-mixpanel` icon. + BRAND_MONDAY (IconName): The :code:`brand-monday` icon. + BRAND_MONGODB (IconName): The :code:`brand-mongodb` icon. + BRAND_MY_OPPO (IconName): The :code:`brand-my-oppo` icon. + BRAND_MYSQL (IconName): The :code:`brand-mysql` icon. + BRAND_NATIONAL_GEOGRAPHIC (IconName): The :code:`brand-national-geographic` icon. + BRAND_NEM (IconName): The :code:`brand-nem` icon. + BRAND_NETBEANS (IconName): The :code:`brand-netbeans` icon. + BRAND_NETEASE_MUSIC (IconName): The :code:`brand-netease-music` icon. + BRAND_NETFLIX (IconName): The :code:`brand-netflix` icon. + BRAND_NEXO (IconName): The :code:`brand-nexo` icon. + BRAND_NEXTCLOUD (IconName): The :code:`brand-nextcloud` icon. + BRAND_NEXTJS (IconName): The :code:`brand-nextjs` icon. + BRAND_NODEJS (IconName): The :code:`brand-nodejs` icon. + BRAND_NORD_VPN (IconName): The :code:`brand-nord-vpn` icon. + BRAND_NOTION (IconName): The :code:`brand-notion` icon. + BRAND_NPM (IconName): The :code:`brand-npm` icon. + BRAND_NUXT (IconName): The :code:`brand-nuxt` icon. + BRAND_NYTIMES (IconName): The :code:`brand-nytimes` icon. + BRAND_OAUTH (IconName): The :code:`brand-oauth` icon. + BRAND_OFFICE (IconName): The :code:`brand-office` icon. + BRAND_OK_RU (IconName): The :code:`brand-ok-ru` icon. + BRAND_ONEDRIVE (IconName): The :code:`brand-onedrive` icon. + BRAND_ONLYFANS (IconName): The :code:`brand-onlyfans` icon. + BRAND_OPEN_SOURCE (IconName): The :code:`brand-open-source` icon. + BRAND_OPENAI (IconName): The :code:`brand-openai` icon. + BRAND_OPENVPN (IconName): The :code:`brand-openvpn` icon. + BRAND_OPERA (IconName): The :code:`brand-opera` icon. + BRAND_PAGEKIT (IconName): The :code:`brand-pagekit` icon. + BRAND_PATREON (IconName): The :code:`brand-patreon` icon. + BRAND_PAYPAL (IconName): The :code:`brand-paypal` icon. + BRAND_PAYPAL_FILLED (IconName): The :code:`brand-paypal-filled` icon. + BRAND_PAYPAY (IconName): The :code:`brand-paypay` icon. + BRAND_PEANUT (IconName): The :code:`brand-peanut` icon. + BRAND_PEPSI (IconName): The :code:`brand-pepsi` icon. + BRAND_PHP (IconName): The :code:`brand-php` icon. + BRAND_PICSART (IconName): The :code:`brand-picsart` icon. + BRAND_PINTEREST (IconName): The :code:`brand-pinterest` icon. + BRAND_PLANETSCALE (IconName): The :code:`brand-planetscale` icon. + BRAND_POCKET (IconName): The :code:`brand-pocket` icon. + BRAND_POLYMER (IconName): The :code:`brand-polymer` icon. + BRAND_POWERSHELL (IconName): The :code:`brand-powershell` icon. + BRAND_PRISMA (IconName): The :code:`brand-prisma` icon. + BRAND_PRODUCTHUNT (IconName): The :code:`brand-producthunt` icon. + BRAND_PUSHBULLET (IconName): The :code:`brand-pushbullet` icon. + BRAND_PUSHOVER (IconName): The :code:`brand-pushover` icon. + BRAND_PYTHON (IconName): The :code:`brand-python` icon. + BRAND_QQ (IconName): The :code:`brand-qq` icon. + BRAND_RADIX_UI (IconName): The :code:`brand-radix-ui` icon. + BRAND_REACT (IconName): The :code:`brand-react` icon. + BRAND_REACT_NATIVE (IconName): The :code:`brand-react-native` icon. + BRAND_REASON (IconName): The :code:`brand-reason` icon. + BRAND_REDDIT (IconName): The :code:`brand-reddit` icon. + BRAND_REDHAT (IconName): The :code:`brand-redhat` icon. + BRAND_REDUX (IconName): The :code:`brand-redux` icon. + BRAND_REVOLUT (IconName): The :code:`brand-revolut` icon. + BRAND_RUMBLE (IconName): The :code:`brand-rumble` icon. + BRAND_RUST (IconName): The :code:`brand-rust` icon. + BRAND_SAFARI (IconName): The :code:`brand-safari` icon. + BRAND_SAMSUNGPASS (IconName): The :code:`brand-samsungpass` icon. + BRAND_SASS (IconName): The :code:`brand-sass` icon. + BRAND_SENTRY (IconName): The :code:`brand-sentry` icon. + BRAND_SHARIK (IconName): The :code:`brand-sharik` icon. + BRAND_SHAZAM (IconName): The :code:`brand-shazam` icon. + BRAND_SHOPEE (IconName): The :code:`brand-shopee` icon. + BRAND_SKETCH (IconName): The :code:`brand-sketch` icon. + BRAND_SKYPE (IconName): The :code:`brand-skype` icon. + BRAND_SLACK (IconName): The :code:`brand-slack` icon. + BRAND_SNAPCHAT (IconName): The :code:`brand-snapchat` icon. + BRAND_SNAPSEED (IconName): The :code:`brand-snapseed` icon. + BRAND_SNOWFLAKE (IconName): The :code:`brand-snowflake` icon. + BRAND_SOCKET_IO (IconName): The :code:`brand-socket-io` icon. + BRAND_SOLIDJS (IconName): The :code:`brand-solidjs` icon. + BRAND_SOUNDCLOUD (IconName): The :code:`brand-soundcloud` icon. + BRAND_SPACEHEY (IconName): The :code:`brand-spacehey` icon. + BRAND_SPEEDTEST (IconName): The :code:`brand-speedtest` icon. + BRAND_SPOTIFY (IconName): The :code:`brand-spotify` icon. + BRAND_STACKOVERFLOW (IconName): The :code:`brand-stackoverflow` icon. + BRAND_STACKSHARE (IconName): The :code:`brand-stackshare` icon. + BRAND_STEAM (IconName): The :code:`brand-steam` icon. + BRAND_STORJ (IconName): The :code:`brand-storj` icon. + BRAND_STORYBOOK (IconName): The :code:`brand-storybook` icon. + BRAND_STORYTEL (IconName): The :code:`brand-storytel` icon. + BRAND_STRAVA (IconName): The :code:`brand-strava` icon. + BRAND_STRIPE (IconName): The :code:`brand-stripe` icon. + BRAND_SUBLIME_TEXT (IconName): The :code:`brand-sublime-text` icon. + BRAND_SUGARIZER (IconName): The :code:`brand-sugarizer` icon. + BRAND_SUPABASE (IconName): The :code:`brand-supabase` icon. + BRAND_SUPERHUMAN (IconName): The :code:`brand-superhuman` icon. + BRAND_SUPERNOVA (IconName): The :code:`brand-supernova` icon. + BRAND_SURFSHARK (IconName): The :code:`brand-surfshark` icon. + BRAND_SVELTE (IconName): The :code:`brand-svelte` icon. + BRAND_SWIFT (IconName): The :code:`brand-swift` icon. + BRAND_SYMFONY (IconName): The :code:`brand-symfony` icon. + BRAND_TABLER (IconName): The :code:`brand-tabler` icon. + BRAND_TAILWIND (IconName): The :code:`brand-tailwind` icon. + BRAND_TAOBAO (IconName): The :code:`brand-taobao` icon. + BRAND_TED (IconName): The :code:`brand-ted` icon. + BRAND_TELEGRAM (IconName): The :code:`brand-telegram` icon. + BRAND_TERRAFORM (IconName): The :code:`brand-terraform` icon. + BRAND_TETHER (IconName): The :code:`brand-tether` icon. + BRAND_THREEJS (IconName): The :code:`brand-threejs` icon. + BRAND_TIDAL (IconName): The :code:`brand-tidal` icon. + BRAND_TIKTO_FILLED (IconName): The :code:`brand-tikto-filled` icon. + BRAND_TIKTOK (IconName): The :code:`brand-tiktok` icon. + BRAND_TINDER (IconName): The :code:`brand-tinder` icon. + BRAND_TOPBUZZ (IconName): The :code:`brand-topbuzz` icon. + BRAND_TORCHAIN (IconName): The :code:`brand-torchain` icon. + BRAND_TOYOTA (IconName): The :code:`brand-toyota` icon. + BRAND_TRELLO (IconName): The :code:`brand-trello` icon. + BRAND_TRIPADVISOR (IconName): The :code:`brand-tripadvisor` icon. + BRAND_TUMBLR (IconName): The :code:`brand-tumblr` icon. + BRAND_TWILIO (IconName): The :code:`brand-twilio` icon. + BRAND_TWITCH (IconName): The :code:`brand-twitch` icon. + BRAND_TWITTER (IconName): The :code:`brand-twitter` icon. + BRAND_TWITTER_FILLED (IconName): The :code:`brand-twitter-filled` icon. + BRAND_TYPESCRIPT (IconName): The :code:`brand-typescript` icon. + BRAND_UBER (IconName): The :code:`brand-uber` icon. + BRAND_UBUNTU (IconName): The :code:`brand-ubuntu` icon. + BRAND_UNITY (IconName): The :code:`brand-unity` icon. + BRAND_UNSPLASH (IconName): The :code:`brand-unsplash` icon. + BRAND_UPWORK (IconName): The :code:`brand-upwork` icon. + BRAND_VALORANT (IconName): The :code:`brand-valorant` icon. + BRAND_VERCEL (IconName): The :code:`brand-vercel` icon. + BRAND_VIMEO (IconName): The :code:`brand-vimeo` icon. + BRAND_VINTED (IconName): The :code:`brand-vinted` icon. + BRAND_VISA (IconName): The :code:`brand-visa` icon. + BRAND_VISUAL_STUDIO (IconName): The :code:`brand-visual-studio` icon. + BRAND_VITE (IconName): The :code:`brand-vite` icon. + BRAND_VIVALDI (IconName): The :code:`brand-vivaldi` icon. + BRAND_VK (IconName): The :code:`brand-vk` icon. + BRAND_VLC (IconName): The :code:`brand-vlc` icon. + BRAND_VOLKSWAGEN (IconName): The :code:`brand-volkswagen` icon. + BRAND_VSCO (IconName): The :code:`brand-vsco` icon. + BRAND_VSCODE (IconName): The :code:`brand-vscode` icon. + BRAND_VUE (IconName): The :code:`brand-vue` icon. + BRAND_WALMART (IconName): The :code:`brand-walmart` icon. + BRAND_WAZE (IconName): The :code:`brand-waze` icon. + BRAND_WEBFLOW (IconName): The :code:`brand-webflow` icon. + BRAND_WECHAT (IconName): The :code:`brand-wechat` icon. + BRAND_WEIBO (IconName): The :code:`brand-weibo` icon. + BRAND_WHATSAPP (IconName): The :code:`brand-whatsapp` icon. + BRAND_WIKIPEDIA (IconName): The :code:`brand-wikipedia` icon. + BRAND_WINDOWS (IconName): The :code:`brand-windows` icon. + BRAND_WINDY (IconName): The :code:`brand-windy` icon. + BRAND_WISH (IconName): The :code:`brand-wish` icon. + BRAND_WIX (IconName): The :code:`brand-wix` icon. + BRAND_WORDPRESS (IconName): The :code:`brand-wordpress` icon. + BRAND_XAMARIN (IconName): The :code:`brand-xamarin` icon. + BRAND_XBOX (IconName): The :code:`brand-xbox` icon. + BRAND_XING (IconName): The :code:`brand-xing` icon. + BRAND_YAHOO (IconName): The :code:`brand-yahoo` icon. + BRAND_YANDEX (IconName): The :code:`brand-yandex` icon. + BRAND_YATSE (IconName): The :code:`brand-yatse` icon. + BRAND_YCOMBINATOR (IconName): The :code:`brand-ycombinator` icon. + BRAND_YOUTUBE (IconName): The :code:`brand-youtube` icon. + BRAND_YOUTUBE_KIDS (IconName): The :code:`brand-youtube-kids` icon. + BRAND_ZALANDO (IconName): The :code:`brand-zalando` icon. + BRAND_ZAPIER (IconName): The :code:`brand-zapier` icon. + BRAND_ZEIT (IconName): The :code:`brand-zeit` icon. + BRAND_ZHIHU (IconName): The :code:`brand-zhihu` icon. + BRAND_ZOOM (IconName): The :code:`brand-zoom` icon. + BRAND_ZULIP (IconName): The :code:`brand-zulip` icon. + BRAND_ZWIFT (IconName): The :code:`brand-zwift` icon. + BREAD (IconName): The :code:`bread` icon. + BREAD_OFF (IconName): The :code:`bread-off` icon. + BRIEFCASE (IconName): The :code:`briefcase` icon. + BRIEFCASE_OFF (IconName): The :code:`briefcase-off` icon. + BRIGHTNESS (IconName): The :code:`brightness` icon. + BRIGHTNESS_2 (IconName): The :code:`brightness-2` icon. + BRIGHTNESS_DOWN (IconName): The :code:`brightness-down` icon. + BRIGHTNESS_HALF (IconName): The :code:`brightness-half` icon. + BRIGHTNESS_OFF (IconName): The :code:`brightness-off` icon. + BRIGHTNESS_UP (IconName): The :code:`brightness-up` icon. + BROADCAST (IconName): The :code:`broadcast` icon. + BROADCAST_OFF (IconName): The :code:`broadcast-off` icon. + BROWSER (IconName): The :code:`browser` icon. + BROWSER_CHECK (IconName): The :code:`browser-check` icon. + BROWSER_OFF (IconName): The :code:`browser-off` icon. + BROWSER_PLUS (IconName): The :code:`browser-plus` icon. + BROWSER_X (IconName): The :code:`browser-x` icon. + BRUSH (IconName): The :code:`brush` icon. + BRUSH_OFF (IconName): The :code:`brush-off` icon. + BUCKET (IconName): The :code:`bucket` icon. + BUCKET_DROPLET (IconName): The :code:`bucket-droplet` icon. + BUCKET_OFF (IconName): The :code:`bucket-off` icon. + BUG (IconName): The :code:`bug` icon. + BUG_OFF (IconName): The :code:`bug-off` icon. + BUILDING (IconName): The :code:`building` icon. + BUILDING_ARCH (IconName): The :code:`building-arch` icon. + BUILDING_BANK (IconName): The :code:`building-bank` icon. + BUILDING_BRIDGE (IconName): The :code:`building-bridge` icon. + BUILDING_BRIDGE_2 (IconName): The :code:`building-bridge-2` icon. + BUILDING_BROADCAST_TOWER (IconName): The :code:`building-broadcast-tower` icon. + BUILDING_CAROUSEL (IconName): The :code:`building-carousel` icon. + BUILDING_CASTLE (IconName): The :code:`building-castle` icon. + BUILDING_CHURCH (IconName): The :code:`building-church` icon. + BUILDING_CIRCUS (IconName): The :code:`building-circus` icon. + BUILDING_COMMUNITY (IconName): The :code:`building-community` icon. + BUILDING_COTTAGE (IconName): The :code:`building-cottage` icon. + BUILDING_ESTATE (IconName): The :code:`building-estate` icon. + BUILDING_FACTORY (IconName): The :code:`building-factory` icon. + BUILDING_FACTORY_2 (IconName): The :code:`building-factory-2` icon. + BUILDING_FORTRESS (IconName): The :code:`building-fortress` icon. + BUILDING_HOSPITAL (IconName): The :code:`building-hospital` icon. + BUILDING_LIGHTHOUSE (IconName): The :code:`building-lighthouse` icon. + BUILDING_MONUMENT (IconName): The :code:`building-monument` icon. + BUILDING_MOSQUE (IconName): The :code:`building-mosque` icon. + BUILDING_PAVILION (IconName): The :code:`building-pavilion` icon. + BUILDING_SKYSCRAPER (IconName): The :code:`building-skyscraper` icon. + BUILDING_STADIUM (IconName): The :code:`building-stadium` icon. + BUILDING_STORE (IconName): The :code:`building-store` icon. + BUILDING_TUNNEL (IconName): The :code:`building-tunnel` icon. + BUILDING_WAREHOUSE (IconName): The :code:`building-warehouse` icon. + BUILDING_WIND_TURBINE (IconName): The :code:`building-wind-turbine` icon. + BULB (IconName): The :code:`bulb` icon. + BULB_FILLED (IconName): The :code:`bulb-filled` icon. + BULB_OFF (IconName): The :code:`bulb-off` icon. + BULLDOZER (IconName): The :code:`bulldozer` icon. + BUS (IconName): The :code:`bus` icon. + BUS_OFF (IconName): The :code:`bus-off` icon. + BUS_STOP (IconName): The :code:`bus-stop` icon. + BUSINESSPLAN (IconName): The :code:`businessplan` icon. + BUTTERFLY (IconName): The :code:`butterfly` icon. + CACTUS (IconName): The :code:`cactus` icon. + CACTUS_OFF (IconName): The :code:`cactus-off` icon. + CAKE (IconName): The :code:`cake` icon. + CAKE_OFF (IconName): The :code:`cake-off` icon. + CALCULATOR (IconName): The :code:`calculator` icon. + CALCULATOR_OFF (IconName): The :code:`calculator-off` icon. + CALENDAR (IconName): The :code:`calendar` icon. + CALENDAR_BOLT (IconName): The :code:`calendar-bolt` icon. + CALENDAR_CANCEL (IconName): The :code:`calendar-cancel` icon. + CALENDAR_CHECK (IconName): The :code:`calendar-check` icon. + CALENDAR_CODE (IconName): The :code:`calendar-code` icon. + CALENDAR_COG (IconName): The :code:`calendar-cog` icon. + CALENDAR_DOLLAR (IconName): The :code:`calendar-dollar` icon. + CALENDAR_DOWN (IconName): The :code:`calendar-down` icon. + CALENDAR_DUE (IconName): The :code:`calendar-due` icon. + CALENDAR_EVENT (IconName): The :code:`calendar-event` icon. + CALENDAR_EXCLAMATION (IconName): The :code:`calendar-exclamation` icon. + CALENDAR_HEART (IconName): The :code:`calendar-heart` icon. + CALENDAR_MINUS (IconName): The :code:`calendar-minus` icon. + CALENDAR_OFF (IconName): The :code:`calendar-off` icon. + CALENDAR_PAUSE (IconName): The :code:`calendar-pause` icon. + CALENDAR_PIN (IconName): The :code:`calendar-pin` icon. + CALENDAR_PLUS (IconName): The :code:`calendar-plus` icon. + CALENDAR_QUESTION (IconName): The :code:`calendar-question` icon. + CALENDAR_REPEAT (IconName): The :code:`calendar-repeat` icon. + CALENDAR_SEARCH (IconName): The :code:`calendar-search` icon. + CALENDAR_SHARE (IconName): The :code:`calendar-share` icon. + CALENDAR_STAR (IconName): The :code:`calendar-star` icon. + CALENDAR_STATS (IconName): The :code:`calendar-stats` icon. + CALENDAR_TIME (IconName): The :code:`calendar-time` icon. + CALENDAR_UP (IconName): The :code:`calendar-up` icon. + CALENDAR_X (IconName): The :code:`calendar-x` icon. + CAMERA (IconName): The :code:`camera` icon. + CAMERA_BOLT (IconName): The :code:`camera-bolt` icon. + CAMERA_CANCEL (IconName): The :code:`camera-cancel` icon. + CAMERA_CHECK (IconName): The :code:`camera-check` icon. + CAMERA_CODE (IconName): The :code:`camera-code` icon. + CAMERA_COG (IconName): The :code:`camera-cog` icon. + CAMERA_DOLLAR (IconName): The :code:`camera-dollar` icon. + CAMERA_DOWN (IconName): The :code:`camera-down` icon. + CAMERA_EXCLAMATION (IconName): The :code:`camera-exclamation` icon. + CAMERA_FILLED (IconName): The :code:`camera-filled` icon. + CAMERA_HEART (IconName): The :code:`camera-heart` icon. + CAMERA_MINUS (IconName): The :code:`camera-minus` icon. + CAMERA_OFF (IconName): The :code:`camera-off` icon. + CAMERA_PAUSE (IconName): The :code:`camera-pause` icon. + CAMERA_PIN (IconName): The :code:`camera-pin` icon. + CAMERA_PLUS (IconName): The :code:`camera-plus` icon. + CAMERA_QUESTION (IconName): The :code:`camera-question` icon. + CAMERA_ROTATE (IconName): The :code:`camera-rotate` icon. + CAMERA_SEARCH (IconName): The :code:`camera-search` icon. + CAMERA_SELFIE (IconName): The :code:`camera-selfie` icon. + CAMERA_SHARE (IconName): The :code:`camera-share` icon. + CAMERA_STAR (IconName): The :code:`camera-star` icon. + CAMERA_UP (IconName): The :code:`camera-up` icon. + CAMERA_X (IconName): The :code:`camera-x` icon. + CAMPER (IconName): The :code:`camper` icon. + CAMPFIRE (IconName): The :code:`campfire` icon. + CANDLE (IconName): The :code:`candle` icon. + CANDY (IconName): The :code:`candy` icon. + CANDY_OFF (IconName): The :code:`candy-off` icon. + CANE (IconName): The :code:`cane` icon. + CANNABIS (IconName): The :code:`cannabis` icon. + CAPSULE (IconName): The :code:`capsule` icon. + CAPSULE_HORIZONTAL (IconName): The :code:`capsule-horizontal` icon. + CAPTURE (IconName): The :code:`capture` icon. + CAPTURE_OFF (IconName): The :code:`capture-off` icon. + CAR (IconName): The :code:`car` icon. + CAR_CRANE (IconName): The :code:`car-crane` icon. + CAR_CRASH (IconName): The :code:`car-crash` icon. + CAR_OFF (IconName): The :code:`car-off` icon. + CAR_TURBINE (IconName): The :code:`car-turbine` icon. + CARAVAN (IconName): The :code:`caravan` icon. + CARDBOARDS (IconName): The :code:`cardboards` icon. + CARDBOARDS_OFF (IconName): The :code:`cardboards-off` icon. + CARDS (IconName): The :code:`cards` icon. + CARET_DOWN (IconName): The :code:`caret-down` icon. + CARET_LEFT (IconName): The :code:`caret-left` icon. + CARET_RIGHT (IconName): The :code:`caret-right` icon. + CARET_UP (IconName): The :code:`caret-up` icon. + CAROUSEL_HORIZONTAL (IconName): The :code:`carousel-horizontal` icon. + CAROUSEL_HORIZONTAL_FILLED (IconName): The :code:`carousel-horizontal-filled` icon. + CAROUSEL_VERTICAL (IconName): The :code:`carousel-vertical` icon. + CAROUSEL_VERTICAL_FILLED (IconName): The :code:`carousel-vertical-filled` icon. + CARROT (IconName): The :code:`carrot` icon. + CARROT_OFF (IconName): The :code:`carrot-off` icon. + CASH (IconName): The :code:`cash` icon. + CASH_BANKNOTE (IconName): The :code:`cash-banknote` icon. + CASH_BANKNOTE_OFF (IconName): The :code:`cash-banknote-off` icon. + CASH_OFF (IconName): The :code:`cash-off` icon. + CAST (IconName): The :code:`cast` icon. + CAST_OFF (IconName): The :code:`cast-off` icon. + CAT (IconName): The :code:`cat` icon. + CATEGORY (IconName): The :code:`category` icon. + CATEGORY_2 (IconName): The :code:`category-2` icon. + CE (IconName): The :code:`ce` icon. + CE_OFF (IconName): The :code:`ce-off` icon. + CELL (IconName): The :code:`cell` icon. + CELL_SIGNAL_1 (IconName): The :code:`cell-signal-1` icon. + CELL_SIGNAL_2 (IconName): The :code:`cell-signal-2` icon. + CELL_SIGNAL_3 (IconName): The :code:`cell-signal-3` icon. + CELL_SIGNAL_4 (IconName): The :code:`cell-signal-4` icon. + CELL_SIGNAL_5 (IconName): The :code:`cell-signal-5` icon. + CELL_SIGNAL_OFF (IconName): The :code:`cell-signal-off` icon. + CERTIFICATE (IconName): The :code:`certificate` icon. + CERTIFICATE_2 (IconName): The :code:`certificate-2` icon. + CERTIFICATE_2_OFF (IconName): The :code:`certificate-2-off` icon. + CERTIFICATE_OFF (IconName): The :code:`certificate-off` icon. + CHAIR_DIRECTOR (IconName): The :code:`chair-director` icon. + CHALKBOARD (IconName): The :code:`chalkboard` icon. + CHALKBOARD_OFF (IconName): The :code:`chalkboard-off` icon. + CHARGING_PILE (IconName): The :code:`charging-pile` icon. + CHART_ARCS (IconName): The :code:`chart-arcs` icon. + CHART_ARCS_3 (IconName): The :code:`chart-arcs-3` icon. + CHART_AREA (IconName): The :code:`chart-area` icon. + CHART_AREA_FILLED (IconName): The :code:`chart-area-filled` icon. + CHART_AREA_LINE (IconName): The :code:`chart-area-line` icon. + CHART_AREA_LINE_FILLED (IconName): The :code:`chart-area-line-filled` icon. + CHART_ARROWS (IconName): The :code:`chart-arrows` icon. + CHART_ARROWS_VERTICAL (IconName): The :code:`chart-arrows-vertical` icon. + CHART_BAR (IconName): The :code:`chart-bar` icon. + CHART_BAR_OFF (IconName): The :code:`chart-bar-off` icon. + CHART_BUBBLE (IconName): The :code:`chart-bubble` icon. + CHART_BUBBLE_FILLED (IconName): The :code:`chart-bubble-filled` icon. + CHART_CANDLE (IconName): The :code:`chart-candle` icon. + CHART_CANDLE_FILLED (IconName): The :code:`chart-candle-filled` icon. + CHART_CIRCLES (IconName): The :code:`chart-circles` icon. + CHART_DONUT (IconName): The :code:`chart-donut` icon. + CHART_DONUT_2 (IconName): The :code:`chart-donut-2` icon. + CHART_DONUT_3 (IconName): The :code:`chart-donut-3` icon. + CHART_DONUT_4 (IconName): The :code:`chart-donut-4` icon. + CHART_DONUT_FILLED (IconName): The :code:`chart-donut-filled` icon. + CHART_DOTS (IconName): The :code:`chart-dots` icon. + CHART_DOTS_2 (IconName): The :code:`chart-dots-2` icon. + CHART_DOTS_3 (IconName): The :code:`chart-dots-3` icon. + CHART_GRID_DOTS (IconName): The :code:`chart-grid-dots` icon. + CHART_HISTOGRAM (IconName): The :code:`chart-histogram` icon. + CHART_INFOGRAPHIC (IconName): The :code:`chart-infographic` icon. + CHART_LINE (IconName): The :code:`chart-line` icon. + CHART_PIE (IconName): The :code:`chart-pie` icon. + CHART_PIE_2 (IconName): The :code:`chart-pie-2` icon. + CHART_PIE_3 (IconName): The :code:`chart-pie-3` icon. + CHART_PIE_4 (IconName): The :code:`chart-pie-4` icon. + CHART_PIE_FILLED (IconName): The :code:`chart-pie-filled` icon. + CHART_PIE_OFF (IconName): The :code:`chart-pie-off` icon. + CHART_PPF (IconName): The :code:`chart-ppf` icon. + CHART_RADAR (IconName): The :code:`chart-radar` icon. + CHART_SANKEY (IconName): The :code:`chart-sankey` icon. + CHART_TREEMAP (IconName): The :code:`chart-treemap` icon. + CHECK (IconName): The :code:`check` icon. + CHECKBOX (IconName): The :code:`checkbox` icon. + CHECKLIST (IconName): The :code:`checklist` icon. + CHECKS (IconName): The :code:`checks` icon. + CHECKUP_LIST (IconName): The :code:`checkup-list` icon. + CHEESE (IconName): The :code:`cheese` icon. + CHEF_HAT (IconName): The :code:`chef-hat` icon. + CHEF_HAT_OFF (IconName): The :code:`chef-hat-off` icon. + CHERRY (IconName): The :code:`cherry` icon. + CHERRY_FILLED (IconName): The :code:`cherry-filled` icon. + CHESS (IconName): The :code:`chess` icon. + CHESS_BISHOP (IconName): The :code:`chess-bishop` icon. + CHESS_BISHOP_FILLED (IconName): The :code:`chess-bishop-filled` icon. + CHESS_FILLED (IconName): The :code:`chess-filled` icon. + CHESS_KING (IconName): The :code:`chess-king` icon. + CHESS_KING_FILLED (IconName): The :code:`chess-king-filled` icon. + CHESS_KNIGHT (IconName): The :code:`chess-knight` icon. + CHESS_KNIGHT_FILLED (IconName): The :code:`chess-knight-filled` icon. + CHESS_QUEEN (IconName): The :code:`chess-queen` icon. + CHESS_QUEEN_FILLED (IconName): The :code:`chess-queen-filled` icon. + CHESS_ROOK (IconName): The :code:`chess-rook` icon. + CHESS_ROOK_FILLED (IconName): The :code:`chess-rook-filled` icon. + CHEVRON_COMPACT_DOWN (IconName): The :code:`chevron-compact-down` icon. + CHEVRON_COMPACT_LEFT (IconName): The :code:`chevron-compact-left` icon. + CHEVRON_COMPACT_RIGHT (IconName): The :code:`chevron-compact-right` icon. + CHEVRON_COMPACT_UP (IconName): The :code:`chevron-compact-up` icon. + CHEVRON_DOWN (IconName): The :code:`chevron-down` icon. + CHEVRON_DOWN_LEFT (IconName): The :code:`chevron-down-left` icon. + CHEVRON_DOWN_RIGHT (IconName): The :code:`chevron-down-right` icon. + CHEVRON_LEFT (IconName): The :code:`chevron-left` icon. + CHEVRON_LEFT_PIPE (IconName): The :code:`chevron-left-pipe` icon. + CHEVRON_RIGHT (IconName): The :code:`chevron-right` icon. + CHEVRON_RIGHT_PIPE (IconName): The :code:`chevron-right-pipe` icon. + CHEVRON_UP (IconName): The :code:`chevron-up` icon. + CHEVRON_UP_LEFT (IconName): The :code:`chevron-up-left` icon. + CHEVRON_UP_RIGHT (IconName): The :code:`chevron-up-right` icon. + CHEVRONS_DOWN (IconName): The :code:`chevrons-down` icon. + CHEVRONS_DOWN_LEFT (IconName): The :code:`chevrons-down-left` icon. + CHEVRONS_DOWN_RIGHT (IconName): The :code:`chevrons-down-right` icon. + CHEVRONS_LEFT (IconName): The :code:`chevrons-left` icon. + CHEVRONS_RIGHT (IconName): The :code:`chevrons-right` icon. + CHEVRONS_UP (IconName): The :code:`chevrons-up` icon. + CHEVRONS_UP_LEFT (IconName): The :code:`chevrons-up-left` icon. + CHEVRONS_UP_RIGHT (IconName): The :code:`chevrons-up-right` icon. + CHISEL (IconName): The :code:`chisel` icon. + CHRISTMAS_TREE (IconName): The :code:`christmas-tree` icon. + CHRISTMAS_TREE_OFF (IconName): The :code:`christmas-tree-off` icon. + CIRCLE (IconName): The :code:`circle` icon. + CIRCLE_0_FILLED (IconName): The :code:`circle-0-filled` icon. + CIRCLE_1_FILLED (IconName): The :code:`circle-1-filled` icon. + CIRCLE_2_FILLED (IconName): The :code:`circle-2-filled` icon. + CIRCLE_3_FILLED (IconName): The :code:`circle-3-filled` icon. + CIRCLE_4_FILLED (IconName): The :code:`circle-4-filled` icon. + CIRCLE_5_FILLED (IconName): The :code:`circle-5-filled` icon. + CIRCLE_6_FILLED (IconName): The :code:`circle-6-filled` icon. + CIRCLE_7_FILLED (IconName): The :code:`circle-7-filled` icon. + CIRCLE_8_FILLED (IconName): The :code:`circle-8-filled` icon. + CIRCLE_9_FILLED (IconName): The :code:`circle-9-filled` icon. + CIRCLE_ARROW_DOWN (IconName): The :code:`circle-arrow-down` icon. + CIRCLE_ARROW_DOWN_FILLED (IconName): The :code:`circle-arrow-down-filled` icon. + CIRCLE_ARROW_DOWN_LEFT (IconName): The :code:`circle-arrow-down-left` icon. + CIRCLE_ARROW_DOWN_LEFT_FILLED (IconName): The :code:`circle-arrow-down-left-filled` icon. + CIRCLE_ARROW_DOWN_RIGHT (IconName): The :code:`circle-arrow-down-right` icon. + CIRCLE_ARROW_DOWN_RIGHT_FILLED (IconName): The :code:`circle-arrow-down-right-filled` icon. + CIRCLE_ARROW_LEFT (IconName): The :code:`circle-arrow-left` icon. + CIRCLE_ARROW_LEFT_FILLED (IconName): The :code:`circle-arrow-left-filled` icon. + CIRCLE_ARROW_RIGHT (IconName): The :code:`circle-arrow-right` icon. + CIRCLE_ARROW_RIGHT_FILLED (IconName): The :code:`circle-arrow-right-filled` icon. + CIRCLE_ARROW_UP (IconName): The :code:`circle-arrow-up` icon. + CIRCLE_ARROW_UP_FILLED (IconName): The :code:`circle-arrow-up-filled` icon. + CIRCLE_ARROW_UP_LEFT (IconName): The :code:`circle-arrow-up-left` icon. + CIRCLE_ARROW_UP_LEFT_FILLED (IconName): The :code:`circle-arrow-up-left-filled` icon. + CIRCLE_ARROW_UP_RIGHT (IconName): The :code:`circle-arrow-up-right` icon. + CIRCLE_ARROW_UP_RIGHT_FILLED (IconName): The :code:`circle-arrow-up-right-filled` icon. + CIRCLE_CARET_DOWN (IconName): The :code:`circle-caret-down` icon. + CIRCLE_CARET_LEFT (IconName): The :code:`circle-caret-left` icon. + CIRCLE_CARET_RIGHT (IconName): The :code:`circle-caret-right` icon. + CIRCLE_CARET_UP (IconName): The :code:`circle-caret-up` icon. + CIRCLE_CHECK (IconName): The :code:`circle-check` icon. + CIRCLE_CHECK_FILLED (IconName): The :code:`circle-check-filled` icon. + CIRCLE_CHEVRON_DOWN (IconName): The :code:`circle-chevron-down` icon. + CIRCLE_CHEVRON_LEFT (IconName): The :code:`circle-chevron-left` icon. + CIRCLE_CHEVRON_RIGHT (IconName): The :code:`circle-chevron-right` icon. + CIRCLE_CHEVRON_UP (IconName): The :code:`circle-chevron-up` icon. + CIRCLE_CHEVRONS_DOWN (IconName): The :code:`circle-chevrons-down` icon. + CIRCLE_CHEVRONS_LEFT (IconName): The :code:`circle-chevrons-left` icon. + CIRCLE_CHEVRONS_RIGHT (IconName): The :code:`circle-chevrons-right` icon. + CIRCLE_CHEVRONS_UP (IconName): The :code:`circle-chevrons-up` icon. + CIRCLE_DASHED (IconName): The :code:`circle-dashed` icon. + CIRCLE_DOT (IconName): The :code:`circle-dot` icon. + CIRCLE_DOT_FILLED (IconName): The :code:`circle-dot-filled` icon. + CIRCLE_DOTTED (IconName): The :code:`circle-dotted` icon. + CIRCLE_FILLED (IconName): The :code:`circle-filled` icon. + CIRCLE_HALF (IconName): The :code:`circle-half` icon. + CIRCLE_HALF_2 (IconName): The :code:`circle-half-2` icon. + CIRCLE_HALF_VERTICAL (IconName): The :code:`circle-half-vertical` icon. + CIRCLE_KEY (IconName): The :code:`circle-key` icon. + CIRCLE_KEY_FILLED (IconName): The :code:`circle-key-filled` icon. + CIRCLE_LETTER_A (IconName): The :code:`circle-letter-a` icon. + CIRCLE_LETTER_B (IconName): The :code:`circle-letter-b` icon. + CIRCLE_LETTER_C (IconName): The :code:`circle-letter-c` icon. + CIRCLE_LETTER_D (IconName): The :code:`circle-letter-d` icon. + CIRCLE_LETTER_E (IconName): The :code:`circle-letter-e` icon. + CIRCLE_LETTER_F (IconName): The :code:`circle-letter-f` icon. + CIRCLE_LETTER_G (IconName): The :code:`circle-letter-g` icon. + CIRCLE_LETTER_H (IconName): The :code:`circle-letter-h` icon. + CIRCLE_LETTER_I (IconName): The :code:`circle-letter-i` icon. + CIRCLE_LETTER_J (IconName): The :code:`circle-letter-j` icon. + CIRCLE_LETTER_K (IconName): The :code:`circle-letter-k` icon. + CIRCLE_LETTER_L (IconName): The :code:`circle-letter-l` icon. + CIRCLE_LETTER_M (IconName): The :code:`circle-letter-m` icon. + CIRCLE_LETTER_N (IconName): The :code:`circle-letter-n` icon. + CIRCLE_LETTER_O (IconName): The :code:`circle-letter-o` icon. + CIRCLE_LETTER_P (IconName): The :code:`circle-letter-p` icon. + CIRCLE_LETTER_Q (IconName): The :code:`circle-letter-q` icon. + CIRCLE_LETTER_R (IconName): The :code:`circle-letter-r` icon. + CIRCLE_LETTER_S (IconName): The :code:`circle-letter-s` icon. + CIRCLE_LETTER_T (IconName): The :code:`circle-letter-t` icon. + CIRCLE_LETTER_U (IconName): The :code:`circle-letter-u` icon. + CIRCLE_LETTER_V (IconName): The :code:`circle-letter-v` icon. + CIRCLE_LETTER_W (IconName): The :code:`circle-letter-w` icon. + CIRCLE_LETTER_X (IconName): The :code:`circle-letter-x` icon. + CIRCLE_LETTER_Y (IconName): The :code:`circle-letter-y` icon. + CIRCLE_LETTER_Z (IconName): The :code:`circle-letter-z` icon. + CIRCLE_MINUS (IconName): The :code:`circle-minus` icon. + CIRCLE_NUMBER_0 (IconName): The :code:`circle-number-0` icon. + CIRCLE_NUMBER_1 (IconName): The :code:`circle-number-1` icon. + CIRCLE_NUMBER_2 (IconName): The :code:`circle-number-2` icon. + CIRCLE_NUMBER_3 (IconName): The :code:`circle-number-3` icon. + CIRCLE_NUMBER_4 (IconName): The :code:`circle-number-4` icon. + CIRCLE_NUMBER_5 (IconName): The :code:`circle-number-5` icon. + CIRCLE_NUMBER_6 (IconName): The :code:`circle-number-6` icon. + CIRCLE_NUMBER_7 (IconName): The :code:`circle-number-7` icon. + CIRCLE_NUMBER_8 (IconName): The :code:`circle-number-8` icon. + CIRCLE_NUMBER_9 (IconName): The :code:`circle-number-9` icon. + CIRCLE_OFF (IconName): The :code:`circle-off` icon. + CIRCLE_PLUS (IconName): The :code:`circle-plus` icon. + CIRCLE_RECTANGLE (IconName): The :code:`circle-rectangle` icon. + CIRCLE_RECTANGLE_OFF (IconName): The :code:`circle-rectangle-off` icon. + CIRCLE_SQUARE (IconName): The :code:`circle-square` icon. + CIRCLE_TRIANGLE (IconName): The :code:`circle-triangle` icon. + CIRCLE_X (IconName): The :code:`circle-x` icon. + CIRCLE_X_FILLED (IconName): The :code:`circle-x-filled` icon. + CIRCLES (IconName): The :code:`circles` icon. + CIRCLES_FILLED (IconName): The :code:`circles-filled` icon. + CIRCLES_RELATION (IconName): The :code:`circles-relation` icon. + CIRCUIT_AMMETER (IconName): The :code:`circuit-ammeter` icon. + CIRCUIT_BATTERY (IconName): The :code:`circuit-battery` icon. + CIRCUIT_BULB (IconName): The :code:`circuit-bulb` icon. + CIRCUIT_CAPACITOR (IconName): The :code:`circuit-capacitor` icon. + CIRCUIT_CAPACITOR_POLARIZED (IconName): The :code:`circuit-capacitor-polarized` icon. + CIRCUIT_CELL (IconName): The :code:`circuit-cell` icon. + CIRCUIT_CELL_PLUS (IconName): The :code:`circuit-cell-plus` icon. + CIRCUIT_CHANGEOVER (IconName): The :code:`circuit-changeover` icon. + CIRCUIT_DIODE (IconName): The :code:`circuit-diode` icon. + CIRCUIT_DIODE_ZENER (IconName): The :code:`circuit-diode-zener` icon. + CIRCUIT_GROUND (IconName): The :code:`circuit-ground` icon. + CIRCUIT_GROUND_DIGITAL (IconName): The :code:`circuit-ground-digital` icon. + CIRCUIT_INDUCTOR (IconName): The :code:`circuit-inductor` icon. + CIRCUIT_MOTOR (IconName): The :code:`circuit-motor` icon. + CIRCUIT_PUSHBUTTON (IconName): The :code:`circuit-pushbutton` icon. + CIRCUIT_RESISTOR (IconName): The :code:`circuit-resistor` icon. + CIRCUIT_SWITCH_CLOSED (IconName): The :code:`circuit-switch-closed` icon. + CIRCUIT_SWITCH_OPEN (IconName): The :code:`circuit-switch-open` icon. + CIRCUIT_VOLTMETER (IconName): The :code:`circuit-voltmeter` icon. + CLEAR_ALL (IconName): The :code:`clear-all` icon. + CLEAR_FORMATTING (IconName): The :code:`clear-formatting` icon. + CLICK (IconName): The :code:`click` icon. + CLIPBOARD (IconName): The :code:`clipboard` icon. + CLIPBOARD_CHECK (IconName): The :code:`clipboard-check` icon. + CLIPBOARD_COPY (IconName): The :code:`clipboard-copy` icon. + CLIPBOARD_DATA (IconName): The :code:`clipboard-data` icon. + CLIPBOARD_HEART (IconName): The :code:`clipboard-heart` icon. + CLIPBOARD_LIST (IconName): The :code:`clipboard-list` icon. + CLIPBOARD_OFF (IconName): The :code:`clipboard-off` icon. + CLIPBOARD_PLUS (IconName): The :code:`clipboard-plus` icon. + CLIPBOARD_TEXT (IconName): The :code:`clipboard-text` icon. + CLIPBOARD_TYPOGRAPHY (IconName): The :code:`clipboard-typography` icon. + CLIPBOARD_X (IconName): The :code:`clipboard-x` icon. + CLOCK (IconName): The :code:`clock` icon. + CLOCK_2 (IconName): The :code:`clock-2` icon. + CLOCK_BOLT (IconName): The :code:`clock-bolt` icon. + CLOCK_CANCEL (IconName): The :code:`clock-cancel` icon. + CLOCK_CHECK (IconName): The :code:`clock-check` icon. + CLOCK_CODE (IconName): The :code:`clock-code` icon. + CLOCK_COG (IconName): The :code:`clock-cog` icon. + CLOCK_DOLLAR (IconName): The :code:`clock-dollar` icon. + CLOCK_DOWN (IconName): The :code:`clock-down` icon. + CLOCK_EDIT (IconName): The :code:`clock-edit` icon. + CLOCK_EXCLAMATION (IconName): The :code:`clock-exclamation` icon. + CLOCK_FILLED (IconName): The :code:`clock-filled` icon. + CLOCK_HEART (IconName): The :code:`clock-heart` icon. + CLOCK_HOUR_1 (IconName): The :code:`clock-hour-1` icon. + CLOCK_HOUR_10 (IconName): The :code:`clock-hour-10` icon. + CLOCK_HOUR_11 (IconName): The :code:`clock-hour-11` icon. + CLOCK_HOUR_12 (IconName): The :code:`clock-hour-12` icon. + CLOCK_HOUR_2 (IconName): The :code:`clock-hour-2` icon. + CLOCK_HOUR_3 (IconName): The :code:`clock-hour-3` icon. + CLOCK_HOUR_4 (IconName): The :code:`clock-hour-4` icon. + CLOCK_HOUR_5 (IconName): The :code:`clock-hour-5` icon. + CLOCK_HOUR_6 (IconName): The :code:`clock-hour-6` icon. + CLOCK_HOUR_7 (IconName): The :code:`clock-hour-7` icon. + CLOCK_HOUR_8 (IconName): The :code:`clock-hour-8` icon. + CLOCK_HOUR_9 (IconName): The :code:`clock-hour-9` icon. + CLOCK_MINUS (IconName): The :code:`clock-minus` icon. + CLOCK_OFF (IconName): The :code:`clock-off` icon. + CLOCK_PAUSE (IconName): The :code:`clock-pause` icon. + CLOCK_PIN (IconName): The :code:`clock-pin` icon. + CLOCK_PLAY (IconName): The :code:`clock-play` icon. + CLOCK_PLUS (IconName): The :code:`clock-plus` icon. + CLOCK_QUESTION (IconName): The :code:`clock-question` icon. + CLOCK_RECORD (IconName): The :code:`clock-record` icon. + CLOCK_SEARCH (IconName): The :code:`clock-search` icon. + CLOCK_SHARE (IconName): The :code:`clock-share` icon. + CLOCK_SHIELD (IconName): The :code:`clock-shield` icon. + CLOCK_STAR (IconName): The :code:`clock-star` icon. + CLOCK_STOP (IconName): The :code:`clock-stop` icon. + CLOCK_UP (IconName): The :code:`clock-up` icon. + CLOCK_X (IconName): The :code:`clock-x` icon. + CLOTHES_RACK (IconName): The :code:`clothes-rack` icon. + CLOTHES_RACK_OFF (IconName): The :code:`clothes-rack-off` icon. + CLOUD (IconName): The :code:`cloud` icon. + CLOUD_BOLT (IconName): The :code:`cloud-bolt` icon. + CLOUD_CANCEL (IconName): The :code:`cloud-cancel` icon. + CLOUD_CHECK (IconName): The :code:`cloud-check` icon. + CLOUD_CODE (IconName): The :code:`cloud-code` icon. + CLOUD_COG (IconName): The :code:`cloud-cog` icon. + CLOUD_COMPUTING (IconName): The :code:`cloud-computing` icon. + CLOUD_DATA_CONNECTION (IconName): The :code:`cloud-data-connection` icon. + CLOUD_DOLLAR (IconName): The :code:`cloud-dollar` icon. + CLOUD_DOWN (IconName): The :code:`cloud-down` icon. + CLOUD_DOWNLOAD (IconName): The :code:`cloud-download` icon. + CLOUD_EXCLAMATION (IconName): The :code:`cloud-exclamation` icon. + CLOUD_FILLED (IconName): The :code:`cloud-filled` icon. + CLOUD_FOG (IconName): The :code:`cloud-fog` icon. + CLOUD_HEART (IconName): The :code:`cloud-heart` icon. + CLOUD_LOCK (IconName): The :code:`cloud-lock` icon. + CLOUD_LOCK_OPEN (IconName): The :code:`cloud-lock-open` icon. + CLOUD_MINUS (IconName): The :code:`cloud-minus` icon. + CLOUD_OFF (IconName): The :code:`cloud-off` icon. + CLOUD_PAUSE (IconName): The :code:`cloud-pause` icon. + CLOUD_PIN (IconName): The :code:`cloud-pin` icon. + CLOUD_PLUS (IconName): The :code:`cloud-plus` icon. + CLOUD_QUESTION (IconName): The :code:`cloud-question` icon. + CLOUD_RAIN (IconName): The :code:`cloud-rain` icon. + CLOUD_SEARCH (IconName): The :code:`cloud-search` icon. + CLOUD_SHARE (IconName): The :code:`cloud-share` icon. + CLOUD_SNOW (IconName): The :code:`cloud-snow` icon. + CLOUD_STAR (IconName): The :code:`cloud-star` icon. + CLOUD_STORM (IconName): The :code:`cloud-storm` icon. + CLOUD_UP (IconName): The :code:`cloud-up` icon. + CLOUD_UPLOAD (IconName): The :code:`cloud-upload` icon. + CLOUD_X (IconName): The :code:`cloud-x` icon. + CLOVER (IconName): The :code:`clover` icon. + CLOVER_2 (IconName): The :code:`clover-2` icon. + CLUBS (IconName): The :code:`clubs` icon. + CLUBS_FILLED (IconName): The :code:`clubs-filled` icon. + CODE (IconName): The :code:`code` icon. + CODE_ASTERIX (IconName): The :code:`code-asterix` icon. + CODE_CIRCLE (IconName): The :code:`code-circle` icon. + CODE_CIRCLE_2 (IconName): The :code:`code-circle-2` icon. + CODE_DOTS (IconName): The :code:`code-dots` icon. + CODE_MINUS (IconName): The :code:`code-minus` icon. + CODE_OFF (IconName): The :code:`code-off` icon. + CODE_PLUS (IconName): The :code:`code-plus` icon. + COFFEE (IconName): The :code:`coffee` icon. + COFFEE_OFF (IconName): The :code:`coffee-off` icon. + COFFIN (IconName): The :code:`coffin` icon. + COIN (IconName): The :code:`coin` icon. + COIN_BITCOIN (IconName): The :code:`coin-bitcoin` icon. + COIN_EURO (IconName): The :code:`coin-euro` icon. + COIN_MONERO (IconName): The :code:`coin-monero` icon. + COIN_OFF (IconName): The :code:`coin-off` icon. + COIN_POUND (IconName): The :code:`coin-pound` icon. + COIN_RUPEE (IconName): The :code:`coin-rupee` icon. + COIN_YEN (IconName): The :code:`coin-yen` icon. + COIN_YUAN (IconName): The :code:`coin-yuan` icon. + COINS (IconName): The :code:`coins` icon. + COLOR_FILTER (IconName): The :code:`color-filter` icon. + COLOR_PICKER (IconName): The :code:`color-picker` icon. + COLOR_PICKER_OFF (IconName): The :code:`color-picker-off` icon. + COLOR_SWATCH (IconName): The :code:`color-swatch` icon. + COLOR_SWATCH_OFF (IconName): The :code:`color-swatch-off` icon. + COLUMN_INSERT_LEFT (IconName): The :code:`column-insert-left` icon. + COLUMN_INSERT_RIGHT (IconName): The :code:`column-insert-right` icon. + COLUMN_REMOVE (IconName): The :code:`column-remove` icon. + COLUMNS (IconName): The :code:`columns` icon. + COLUMNS_1 (IconName): The :code:`columns-1` icon. + COLUMNS_2 (IconName): The :code:`columns-2` icon. + COLUMNS_3 (IconName): The :code:`columns-3` icon. + COLUMNS_OFF (IconName): The :code:`columns-off` icon. + COMET (IconName): The :code:`comet` icon. + COMMAND (IconName): The :code:`command` icon. + COMMAND_OFF (IconName): The :code:`command-off` icon. + COMPASS (IconName): The :code:`compass` icon. + COMPASS_OFF (IconName): The :code:`compass-off` icon. + COMPONENTS (IconName): The :code:`components` icon. + COMPONENTS_OFF (IconName): The :code:`components-off` icon. + CONE (IconName): The :code:`cone` icon. + CONE_2 (IconName): The :code:`cone-2` icon. + CONE_OFF (IconName): The :code:`cone-off` icon. + CONE_PLUS (IconName): The :code:`cone-plus` icon. + CONFETTI (IconName): The :code:`confetti` icon. + CONFETTI_OFF (IconName): The :code:`confetti-off` icon. + CONFUCIUS (IconName): The :code:`confucius` icon. + CONTAINER (IconName): The :code:`container` icon. + CONTAINER_OFF (IconName): The :code:`container-off` icon. + CONTRAST (IconName): The :code:`contrast` icon. + CONTRAST_2 (IconName): The :code:`contrast-2` icon. + CONTRAST_2_OFF (IconName): The :code:`contrast-2-off` icon. + CONTRAST_OFF (IconName): The :code:`contrast-off` icon. + COOKER (IconName): The :code:`cooker` icon. + COOKIE (IconName): The :code:`cookie` icon. + COOKIE_MAN (IconName): The :code:`cookie-man` icon. + COOKIE_OFF (IconName): The :code:`cookie-off` icon. + COPY (IconName): The :code:`copy` icon. + COPY_OFF (IconName): The :code:`copy-off` icon. + COPYLEFT (IconName): The :code:`copyleft` icon. + COPYLEFT_FILLED (IconName): The :code:`copyleft-filled` icon. + COPYLEFT_OFF (IconName): The :code:`copyleft-off` icon. + COPYRIGHT (IconName): The :code:`copyright` icon. + COPYRIGHT_FILLED (IconName): The :code:`copyright-filled` icon. + COPYRIGHT_OFF (IconName): The :code:`copyright-off` icon. + CORNER_DOWN_LEFT (IconName): The :code:`corner-down-left` icon. + CORNER_DOWN_LEFT_DOUBLE (IconName): The :code:`corner-down-left-double` icon. + CORNER_DOWN_RIGHT (IconName): The :code:`corner-down-right` icon. + CORNER_DOWN_RIGHT_DOUBLE (IconName): The :code:`corner-down-right-double` icon. + CORNER_LEFT_DOWN (IconName): The :code:`corner-left-down` icon. + CORNER_LEFT_DOWN_DOUBLE (IconName): The :code:`corner-left-down-double` icon. + CORNER_LEFT_UP (IconName): The :code:`corner-left-up` icon. + CORNER_LEFT_UP_DOUBLE (IconName): The :code:`corner-left-up-double` icon. + CORNER_RIGHT_DOWN (IconName): The :code:`corner-right-down` icon. + CORNER_RIGHT_DOWN_DOUBLE (IconName): The :code:`corner-right-down-double` icon. + CORNER_RIGHT_UP (IconName): The :code:`corner-right-up` icon. + CORNER_RIGHT_UP_DOUBLE (IconName): The :code:`corner-right-up-double` icon. + CORNER_UP_LEFT (IconName): The :code:`corner-up-left` icon. + CORNER_UP_LEFT_DOUBLE (IconName): The :code:`corner-up-left-double` icon. + CORNER_UP_RIGHT (IconName): The :code:`corner-up-right` icon. + CORNER_UP_RIGHT_DOUBLE (IconName): The :code:`corner-up-right-double` icon. + CPU (IconName): The :code:`cpu` icon. + CPU_2 (IconName): The :code:`cpu-2` icon. + CPU_OFF (IconName): The :code:`cpu-off` icon. + CRANE (IconName): The :code:`crane` icon. + CRANE_OFF (IconName): The :code:`crane-off` icon. + CREATIVE_COMMONS (IconName): The :code:`creative-commons` icon. + CREATIVE_COMMONS_BY (IconName): The :code:`creative-commons-by` icon. + CREATIVE_COMMONS_NC (IconName): The :code:`creative-commons-nc` icon. + CREATIVE_COMMONS_ND (IconName): The :code:`creative-commons-nd` icon. + CREATIVE_COMMONS_OFF (IconName): The :code:`creative-commons-off` icon. + CREATIVE_COMMONS_SA (IconName): The :code:`creative-commons-sa` icon. + CREATIVE_COMMONS_ZERO (IconName): The :code:`creative-commons-zero` icon. + CREDIT_CARD (IconName): The :code:`credit-card` icon. + CREDIT_CARD_OFF (IconName): The :code:`credit-card-off` icon. + CRICKET (IconName): The :code:`cricket` icon. + CROP (IconName): The :code:`crop` icon. + CROSS (IconName): The :code:`cross` icon. + CROSS_FILLED (IconName): The :code:`cross-filled` icon. + CROSS_OFF (IconName): The :code:`cross-off` icon. + CROSSHAIR (IconName): The :code:`crosshair` icon. + CROWN (IconName): The :code:`crown` icon. + CROWN_OFF (IconName): The :code:`crown-off` icon. + CRUTCHES (IconName): The :code:`crutches` icon. + CRUTCHES_OFF (IconName): The :code:`crutches-off` icon. + CRYSTAL_BALL (IconName): The :code:`crystal-ball` icon. + CSV (IconName): The :code:`csv` icon. + CUBE (IconName): The :code:`cube` icon. + CUBE_OFF (IconName): The :code:`cube-off` icon. + CUBE_PLUS (IconName): The :code:`cube-plus` icon. + CUBE_SEND (IconName): The :code:`cube-send` icon. + CUBE_UNFOLDED (IconName): The :code:`cube-unfolded` icon. + CUP (IconName): The :code:`cup` icon. + CUP_OFF (IconName): The :code:`cup-off` icon. + CURLING (IconName): The :code:`curling` icon. + CURLY_LOOP (IconName): The :code:`curly-loop` icon. + CURRENCY (IconName): The :code:`currency` icon. + CURRENCY_AFGHANI (IconName): The :code:`currency-afghani` icon. + CURRENCY_BAHRAINI (IconName): The :code:`currency-bahraini` icon. + CURRENCY_BAHT (IconName): The :code:`currency-baht` icon. + CURRENCY_BITCOIN (IconName): The :code:`currency-bitcoin` icon. + CURRENCY_CENT (IconName): The :code:`currency-cent` icon. + CURRENCY_DINAR (IconName): The :code:`currency-dinar` icon. + CURRENCY_DIRHAM (IconName): The :code:`currency-dirham` icon. + CURRENCY_DOGECOIN (IconName): The :code:`currency-dogecoin` icon. + CURRENCY_DOLLAR (IconName): The :code:`currency-dollar` icon. + CURRENCY_DOLLAR_AUSTRALIAN (IconName): The :code:`currency-dollar-australian` icon. + CURRENCY_DOLLAR_BRUNEI (IconName): The :code:`currency-dollar-brunei` icon. + CURRENCY_DOLLAR_CANADIAN (IconName): The :code:`currency-dollar-canadian` icon. + CURRENCY_DOLLAR_GUYANESE (IconName): The :code:`currency-dollar-guyanese` icon. + CURRENCY_DOLLAR_OFF (IconName): The :code:`currency-dollar-off` icon. + CURRENCY_DOLLAR_SINGAPORE (IconName): The :code:`currency-dollar-singapore` icon. + CURRENCY_DOLLAR_ZIMBABWEAN (IconName): The :code:`currency-dollar-zimbabwean` icon. + CURRENCY_DONG (IconName): The :code:`currency-dong` icon. + CURRENCY_DRAM (IconName): The :code:`currency-dram` icon. + CURRENCY_ETHEREUM (IconName): The :code:`currency-ethereum` icon. + CURRENCY_EURO (IconName): The :code:`currency-euro` icon. + CURRENCY_EURO_OFF (IconName): The :code:`currency-euro-off` icon. + CURRENCY_FLORIN (IconName): The :code:`currency-florin` icon. + CURRENCY_FORINT (IconName): The :code:`currency-forint` icon. + CURRENCY_FRANK (IconName): The :code:`currency-frank` icon. + CURRENCY_GUARANI (IconName): The :code:`currency-guarani` icon. + CURRENCY_HRYVNIA (IconName): The :code:`currency-hryvnia` icon. + CURRENCY_IRANIAN_RIAL (IconName): The :code:`currency-iranian-rial` icon. + CURRENCY_KIP (IconName): The :code:`currency-kip` icon. + CURRENCY_KRONE_CZECH (IconName): The :code:`currency-krone-czech` icon. + CURRENCY_KRONE_DANISH (IconName): The :code:`currency-krone-danish` icon. + CURRENCY_KRONE_SWEDISH (IconName): The :code:`currency-krone-swedish` icon. + CURRENCY_LARI (IconName): The :code:`currency-lari` icon. + CURRENCY_LEU (IconName): The :code:`currency-leu` icon. + CURRENCY_LIRA (IconName): The :code:`currency-lira` icon. + CURRENCY_LITECOIN (IconName): The :code:`currency-litecoin` icon. + CURRENCY_LYD (IconName): The :code:`currency-lyd` icon. + CURRENCY_MANAT (IconName): The :code:`currency-manat` icon. + CURRENCY_MONERO (IconName): The :code:`currency-monero` icon. + CURRENCY_NAIRA (IconName): The :code:`currency-naira` icon. + CURRENCY_NANO (IconName): The :code:`currency-nano` icon. + CURRENCY_OFF (IconName): The :code:`currency-off` icon. + CURRENCY_PAANGA (IconName): The :code:`currency-paanga` icon. + CURRENCY_PESO (IconName): The :code:`currency-peso` icon. + CURRENCY_POUND (IconName): The :code:`currency-pound` icon. + CURRENCY_POUND_OFF (IconName): The :code:`currency-pound-off` icon. + CURRENCY_QUETZAL (IconName): The :code:`currency-quetzal` icon. + CURRENCY_REAL (IconName): The :code:`currency-real` icon. + CURRENCY_RENMINBI (IconName): The :code:`currency-renminbi` icon. + CURRENCY_RIPPLE (IconName): The :code:`currency-ripple` icon. + CURRENCY_RIYAL (IconName): The :code:`currency-riyal` icon. + CURRENCY_RUBEL (IconName): The :code:`currency-rubel` icon. + CURRENCY_RUFIYAA (IconName): The :code:`currency-rufiyaa` icon. + CURRENCY_RUPEE (IconName): The :code:`currency-rupee` icon. + CURRENCY_RUPEE_NEPALESE (IconName): The :code:`currency-rupee-nepalese` icon. + CURRENCY_SHEKEL (IconName): The :code:`currency-shekel` icon. + CURRENCY_SOLANA (IconName): The :code:`currency-solana` icon. + CURRENCY_SOM (IconName): The :code:`currency-som` icon. + CURRENCY_TAKA (IconName): The :code:`currency-taka` icon. + CURRENCY_TENGE (IconName): The :code:`currency-tenge` icon. + CURRENCY_TUGRIK (IconName): The :code:`currency-tugrik` icon. + CURRENCY_WON (IconName): The :code:`currency-won` icon. + CURRENCY_YEN (IconName): The :code:`currency-yen` icon. + CURRENCY_YEN_OFF (IconName): The :code:`currency-yen-off` icon. + CURRENCY_YUAN (IconName): The :code:`currency-yuan` icon. + CURRENCY_ZLOTY (IconName): The :code:`currency-zloty` icon. + CURRENT_LOCATION (IconName): The :code:`current-location` icon. + CURRENT_LOCATION_OFF (IconName): The :code:`current-location-off` icon. + CURSOR_OFF (IconName): The :code:`cursor-off` icon. + CURSOR_TEXT (IconName): The :code:`cursor-text` icon. + CUT (IconName): The :code:`cut` icon. + CYLINDER (IconName): The :code:`cylinder` icon. + CYLINDER_OFF (IconName): The :code:`cylinder-off` icon. + CYLINDER_PLUS (IconName): The :code:`cylinder-plus` icon. + DASHBOARD (IconName): The :code:`dashboard` icon. + DASHBOARD_OFF (IconName): The :code:`dashboard-off` icon. + DATABASE (IconName): The :code:`database` icon. + DATABASE_COG (IconName): The :code:`database-cog` icon. + DATABASE_DOLLAR (IconName): The :code:`database-dollar` icon. + DATABASE_EDIT (IconName): The :code:`database-edit` icon. + DATABASE_EXCLAMATION (IconName): The :code:`database-exclamation` icon. + DATABASE_EXPORT (IconName): The :code:`database-export` icon. + DATABASE_HEART (IconName): The :code:`database-heart` icon. + DATABASE_IMPORT (IconName): The :code:`database-import` icon. + DATABASE_LEAK (IconName): The :code:`database-leak` icon. + DATABASE_MINUS (IconName): The :code:`database-minus` icon. + DATABASE_OFF (IconName): The :code:`database-off` icon. + DATABASE_PLUS (IconName): The :code:`database-plus` icon. + DATABASE_SEARCH (IconName): The :code:`database-search` icon. + DATABASE_SHARE (IconName): The :code:`database-share` icon. + DATABASE_STAR (IconName): The :code:`database-star` icon. + DATABASE_X (IconName): The :code:`database-x` icon. + DECIMAL (IconName): The :code:`decimal` icon. + DEER (IconName): The :code:`deer` icon. + DELTA (IconName): The :code:`delta` icon. + DENTAL (IconName): The :code:`dental` icon. + DENTAL_BROKEN (IconName): The :code:`dental-broken` icon. + DENTAL_OFF (IconName): The :code:`dental-off` icon. + DESELECT (IconName): The :code:`deselect` icon. + DETAILS (IconName): The :code:`details` icon. + DETAILS_OFF (IconName): The :code:`details-off` icon. + DEVICE_AIRPODS (IconName): The :code:`device-airpods` icon. + DEVICE_AIRPODS_CASE (IconName): The :code:`device-airpods-case` icon. + DEVICE_AIRTAG (IconName): The :code:`device-airtag` icon. + DEVICE_ANALYTICS (IconName): The :code:`device-analytics` icon. + DEVICE_AUDIO_TAPE (IconName): The :code:`device-audio-tape` icon. + DEVICE_CAMERA_PHONE (IconName): The :code:`device-camera-phone` icon. + DEVICE_CCTV (IconName): The :code:`device-cctv` icon. + DEVICE_CCTV_OFF (IconName): The :code:`device-cctv-off` icon. + DEVICE_COMPUTER_CAMERA (IconName): The :code:`device-computer-camera` icon. + DEVICE_COMPUTER_CAMERA_OFF (IconName): The :code:`device-computer-camera-off` icon. + DEVICE_DESKTOP (IconName): The :code:`device-desktop` icon. + DEVICE_DESKTOP_ANALYTICS (IconName): The :code:`device-desktop-analytics` icon. + DEVICE_DESKTOP_BOLT (IconName): The :code:`device-desktop-bolt` icon. + DEVICE_DESKTOP_CANCEL (IconName): The :code:`device-desktop-cancel` icon. + DEVICE_DESKTOP_CHECK (IconName): The :code:`device-desktop-check` icon. + DEVICE_DESKTOP_CODE (IconName): The :code:`device-desktop-code` icon. + DEVICE_DESKTOP_COG (IconName): The :code:`device-desktop-cog` icon. + DEVICE_DESKTOP_DOLLAR (IconName): The :code:`device-desktop-dollar` icon. + DEVICE_DESKTOP_DOWN (IconName): The :code:`device-desktop-down` icon. + DEVICE_DESKTOP_EXCLAMATION (IconName): The :code:`device-desktop-exclamation` icon. + DEVICE_DESKTOP_HEART (IconName): The :code:`device-desktop-heart` icon. + DEVICE_DESKTOP_MINUS (IconName): The :code:`device-desktop-minus` icon. + DEVICE_DESKTOP_OFF (IconName): The :code:`device-desktop-off` icon. + DEVICE_DESKTOP_PAUSE (IconName): The :code:`device-desktop-pause` icon. + DEVICE_DESKTOP_PIN (IconName): The :code:`device-desktop-pin` icon. + DEVICE_DESKTOP_PLUS (IconName): The :code:`device-desktop-plus` icon. + DEVICE_DESKTOP_QUESTION (IconName): The :code:`device-desktop-question` icon. + DEVICE_DESKTOP_SEARCH (IconName): The :code:`device-desktop-search` icon. + DEVICE_DESKTOP_SHARE (IconName): The :code:`device-desktop-share` icon. + DEVICE_DESKTOP_STAR (IconName): The :code:`device-desktop-star` icon. + DEVICE_DESKTOP_UP (IconName): The :code:`device-desktop-up` icon. + DEVICE_DESKTOP_X (IconName): The :code:`device-desktop-x` icon. + DEVICE_FLOPPY (IconName): The :code:`device-floppy` icon. + DEVICE_GAMEPAD (IconName): The :code:`device-gamepad` icon. + DEVICE_GAMEPAD_2 (IconName): The :code:`device-gamepad-2` icon. + DEVICE_HEART_MONITOR (IconName): The :code:`device-heart-monitor` icon. + DEVICE_HEART_MONITOR_FILLED (IconName): The :code:`device-heart-monitor-filled` icon. + DEVICE_IMAC (IconName): The :code:`device-imac` icon. + DEVICE_IMAC_BOLT (IconName): The :code:`device-imac-bolt` icon. + DEVICE_IMAC_CANCEL (IconName): The :code:`device-imac-cancel` icon. + DEVICE_IMAC_CHECK (IconName): The :code:`device-imac-check` icon. + DEVICE_IMAC_CODE (IconName): The :code:`device-imac-code` icon. + DEVICE_IMAC_COG (IconName): The :code:`device-imac-cog` icon. + DEVICE_IMAC_DOLLAR (IconName): The :code:`device-imac-dollar` icon. + DEVICE_IMAC_DOWN (IconName): The :code:`device-imac-down` icon. + DEVICE_IMAC_EXCLAMATION (IconName): The :code:`device-imac-exclamation` icon. + DEVICE_IMAC_HEART (IconName): The :code:`device-imac-heart` icon. + DEVICE_IMAC_MINUS (IconName): The :code:`device-imac-minus` icon. + DEVICE_IMAC_OFF (IconName): The :code:`device-imac-off` icon. + DEVICE_IMAC_PAUSE (IconName): The :code:`device-imac-pause` icon. + DEVICE_IMAC_PIN (IconName): The :code:`device-imac-pin` icon. + DEVICE_IMAC_PLUS (IconName): The :code:`device-imac-plus` icon. + DEVICE_IMAC_QUESTION (IconName): The :code:`device-imac-question` icon. + DEVICE_IMAC_SEARCH (IconName): The :code:`device-imac-search` icon. + DEVICE_IMAC_SHARE (IconName): The :code:`device-imac-share` icon. + DEVICE_IMAC_STAR (IconName): The :code:`device-imac-star` icon. + DEVICE_IMAC_UP (IconName): The :code:`device-imac-up` icon. + DEVICE_IMAC_X (IconName): The :code:`device-imac-x` icon. + DEVICE_IPAD (IconName): The :code:`device-ipad` icon. + DEVICE_IPAD_BOLT (IconName): The :code:`device-ipad-bolt` icon. + DEVICE_IPAD_CANCEL (IconName): The :code:`device-ipad-cancel` icon. + DEVICE_IPAD_CHECK (IconName): The :code:`device-ipad-check` icon. + DEVICE_IPAD_CODE (IconName): The :code:`device-ipad-code` icon. + DEVICE_IPAD_COG (IconName): The :code:`device-ipad-cog` icon. + DEVICE_IPAD_DOLLAR (IconName): The :code:`device-ipad-dollar` icon. + DEVICE_IPAD_DOWN (IconName): The :code:`device-ipad-down` icon. + DEVICE_IPAD_EXCLAMATION (IconName): The :code:`device-ipad-exclamation` icon. + DEVICE_IPAD_HEART (IconName): The :code:`device-ipad-heart` icon. + DEVICE_IPAD_HORIZONTAL (IconName): The :code:`device-ipad-horizontal` icon. + DEVICE_IPAD_HORIZONTAL_BOLT (IconName): The :code:`device-ipad-horizontal-bolt` icon. + DEVICE_IPAD_HORIZONTAL_CANCEL (IconName): The :code:`device-ipad-horizontal-cancel` icon. + DEVICE_IPAD_HORIZONTAL_CHECK (IconName): The :code:`device-ipad-horizontal-check` icon. + DEVICE_IPAD_HORIZONTAL_CODE (IconName): The :code:`device-ipad-horizontal-code` icon. + DEVICE_IPAD_HORIZONTAL_COG (IconName): The :code:`device-ipad-horizontal-cog` icon. + DEVICE_IPAD_HORIZONTAL_DOLLAR (IconName): The :code:`device-ipad-horizontal-dollar` icon. + DEVICE_IPAD_HORIZONTAL_DOWN (IconName): The :code:`device-ipad-horizontal-down` icon. + DEVICE_IPAD_HORIZONTAL_EXCLAMATION (IconName): The :code:`device-ipad-horizontal-exclamation` icon. + DEVICE_IPAD_HORIZONTAL_HEART (IconName): The :code:`device-ipad-horizontal-heart` icon. + DEVICE_IPAD_HORIZONTAL_MINUS (IconName): The :code:`device-ipad-horizontal-minus` icon. + DEVICE_IPAD_HORIZONTAL_OFF (IconName): The :code:`device-ipad-horizontal-off` icon. + DEVICE_IPAD_HORIZONTAL_PAUSE (IconName): The :code:`device-ipad-horizontal-pause` icon. + DEVICE_IPAD_HORIZONTAL_PIN (IconName): The :code:`device-ipad-horizontal-pin` icon. + DEVICE_IPAD_HORIZONTAL_PLUS (IconName): The :code:`device-ipad-horizontal-plus` icon. + DEVICE_IPAD_HORIZONTAL_QUESTION (IconName): The :code:`device-ipad-horizontal-question` icon. + DEVICE_IPAD_HORIZONTAL_SEARCH (IconName): The :code:`device-ipad-horizontal-search` icon. + DEVICE_IPAD_HORIZONTAL_SHARE (IconName): The :code:`device-ipad-horizontal-share` icon. + DEVICE_IPAD_HORIZONTAL_STAR (IconName): The :code:`device-ipad-horizontal-star` icon. + DEVICE_IPAD_HORIZONTAL_UP (IconName): The :code:`device-ipad-horizontal-up` icon. + DEVICE_IPAD_HORIZONTAL_X (IconName): The :code:`device-ipad-horizontal-x` icon. + DEVICE_IPAD_MINUS (IconName): The :code:`device-ipad-minus` icon. + DEVICE_IPAD_OFF (IconName): The :code:`device-ipad-off` icon. + DEVICE_IPAD_PAUSE (IconName): The :code:`device-ipad-pause` icon. + DEVICE_IPAD_PIN (IconName): The :code:`device-ipad-pin` icon. + DEVICE_IPAD_PLUS (IconName): The :code:`device-ipad-plus` icon. + DEVICE_IPAD_QUESTION (IconName): The :code:`device-ipad-question` icon. + DEVICE_IPAD_SEARCH (IconName): The :code:`device-ipad-search` icon. + DEVICE_IPAD_SHARE (IconName): The :code:`device-ipad-share` icon. + DEVICE_IPAD_STAR (IconName): The :code:`device-ipad-star` icon. + DEVICE_IPAD_UP (IconName): The :code:`device-ipad-up` icon. + DEVICE_IPAD_X (IconName): The :code:`device-ipad-x` icon. + DEVICE_LANDLINE_PHONE (IconName): The :code:`device-landline-phone` icon. + DEVICE_LAPTOP (IconName): The :code:`device-laptop` icon. + DEVICE_LAPTOP_OFF (IconName): The :code:`device-laptop-off` icon. + DEVICE_MOBILE (IconName): The :code:`device-mobile` icon. + DEVICE_MOBILE_BOLT (IconName): The :code:`device-mobile-bolt` icon. + DEVICE_MOBILE_CANCEL (IconName): The :code:`device-mobile-cancel` icon. + DEVICE_MOBILE_CHARGING (IconName): The :code:`device-mobile-charging` icon. + DEVICE_MOBILE_CHECK (IconName): The :code:`device-mobile-check` icon. + DEVICE_MOBILE_CODE (IconName): The :code:`device-mobile-code` icon. + DEVICE_MOBILE_COG (IconName): The :code:`device-mobile-cog` icon. + DEVICE_MOBILE_DOLLAR (IconName): The :code:`device-mobile-dollar` icon. + DEVICE_MOBILE_DOWN (IconName): The :code:`device-mobile-down` icon. + DEVICE_MOBILE_EXCLAMATION (IconName): The :code:`device-mobile-exclamation` icon. + DEVICE_MOBILE_FILLED (IconName): The :code:`device-mobile-filled` icon. + DEVICE_MOBILE_HEART (IconName): The :code:`device-mobile-heart` icon. + DEVICE_MOBILE_MESSAGE (IconName): The :code:`device-mobile-message` icon. + DEVICE_MOBILE_MINUS (IconName): The :code:`device-mobile-minus` icon. + DEVICE_MOBILE_OFF (IconName): The :code:`device-mobile-off` icon. + DEVICE_MOBILE_PAUSE (IconName): The :code:`device-mobile-pause` icon. + DEVICE_MOBILE_PIN (IconName): The :code:`device-mobile-pin` icon. + DEVICE_MOBILE_PLUS (IconName): The :code:`device-mobile-plus` icon. + DEVICE_MOBILE_QUESTION (IconName): The :code:`device-mobile-question` icon. + DEVICE_MOBILE_ROTATED (IconName): The :code:`device-mobile-rotated` icon. + DEVICE_MOBILE_SEARCH (IconName): The :code:`device-mobile-search` icon. + DEVICE_MOBILE_SHARE (IconName): The :code:`device-mobile-share` icon. + DEVICE_MOBILE_STAR (IconName): The :code:`device-mobile-star` icon. + DEVICE_MOBILE_UP (IconName): The :code:`device-mobile-up` icon. + DEVICE_MOBILE_VIBRATION (IconName): The :code:`device-mobile-vibration` icon. + DEVICE_MOBILE_X (IconName): The :code:`device-mobile-x` icon. + DEVICE_NINTENDO (IconName): The :code:`device-nintendo` icon. + DEVICE_NINTENDO_OFF (IconName): The :code:`device-nintendo-off` icon. + DEVICE_REMOTE (IconName): The :code:`device-remote` icon. + DEVICE_SD_CARD (IconName): The :code:`device-sd-card` icon. + DEVICE_SIM (IconName): The :code:`device-sim` icon. + DEVICE_SIM_1 (IconName): The :code:`device-sim-1` icon. + DEVICE_SIM_2 (IconName): The :code:`device-sim-2` icon. + DEVICE_SIM_3 (IconName): The :code:`device-sim-3` icon. + DEVICE_SPEAKER (IconName): The :code:`device-speaker` icon. + DEVICE_SPEAKER_OFF (IconName): The :code:`device-speaker-off` icon. + DEVICE_TABLET (IconName): The :code:`device-tablet` icon. + DEVICE_TABLET_BOLT (IconName): The :code:`device-tablet-bolt` icon. + DEVICE_TABLET_CANCEL (IconName): The :code:`device-tablet-cancel` icon. + DEVICE_TABLET_CHECK (IconName): The :code:`device-tablet-check` icon. + DEVICE_TABLET_CODE (IconName): The :code:`device-tablet-code` icon. + DEVICE_TABLET_COG (IconName): The :code:`device-tablet-cog` icon. + DEVICE_TABLET_DOLLAR (IconName): The :code:`device-tablet-dollar` icon. + DEVICE_TABLET_DOWN (IconName): The :code:`device-tablet-down` icon. + DEVICE_TABLET_EXCLAMATION (IconName): The :code:`device-tablet-exclamation` icon. + DEVICE_TABLET_FILLED (IconName): The :code:`device-tablet-filled` icon. + DEVICE_TABLET_HEART (IconName): The :code:`device-tablet-heart` icon. + DEVICE_TABLET_MINUS (IconName): The :code:`device-tablet-minus` icon. + DEVICE_TABLET_OFF (IconName): The :code:`device-tablet-off` icon. + DEVICE_TABLET_PAUSE (IconName): The :code:`device-tablet-pause` icon. + DEVICE_TABLET_PIN (IconName): The :code:`device-tablet-pin` icon. + DEVICE_TABLET_PLUS (IconName): The :code:`device-tablet-plus` icon. + DEVICE_TABLET_QUESTION (IconName): The :code:`device-tablet-question` icon. + DEVICE_TABLET_SEARCH (IconName): The :code:`device-tablet-search` icon. + DEVICE_TABLET_SHARE (IconName): The :code:`device-tablet-share` icon. + DEVICE_TABLET_STAR (IconName): The :code:`device-tablet-star` icon. + DEVICE_TABLET_UP (IconName): The :code:`device-tablet-up` icon. + DEVICE_TABLET_X (IconName): The :code:`device-tablet-x` icon. + DEVICE_TV (IconName): The :code:`device-tv` icon. + DEVICE_TV_OFF (IconName): The :code:`device-tv-off` icon. + DEVICE_TV_OLD (IconName): The :code:`device-tv-old` icon. + DEVICE_VISION_PRO (IconName): The :code:`device-vision-pro` icon. + DEVICE_WATCH (IconName): The :code:`device-watch` icon. + DEVICE_WATCH_BOLT (IconName): The :code:`device-watch-bolt` icon. + DEVICE_WATCH_CANCEL (IconName): The :code:`device-watch-cancel` icon. + DEVICE_WATCH_CHECK (IconName): The :code:`device-watch-check` icon. + DEVICE_WATCH_CODE (IconName): The :code:`device-watch-code` icon. + DEVICE_WATCH_COG (IconName): The :code:`device-watch-cog` icon. + DEVICE_WATCH_DOLLAR (IconName): The :code:`device-watch-dollar` icon. + DEVICE_WATCH_DOWN (IconName): The :code:`device-watch-down` icon. + DEVICE_WATCH_EXCLAMATION (IconName): The :code:`device-watch-exclamation` icon. + DEVICE_WATCH_HEART (IconName): The :code:`device-watch-heart` icon. + DEVICE_WATCH_MINUS (IconName): The :code:`device-watch-minus` icon. + DEVICE_WATCH_OFF (IconName): The :code:`device-watch-off` icon. + DEVICE_WATCH_PAUSE (IconName): The :code:`device-watch-pause` icon. + DEVICE_WATCH_PIN (IconName): The :code:`device-watch-pin` icon. + DEVICE_WATCH_PLUS (IconName): The :code:`device-watch-plus` icon. + DEVICE_WATCH_QUESTION (IconName): The :code:`device-watch-question` icon. + DEVICE_WATCH_SEARCH (IconName): The :code:`device-watch-search` icon. + DEVICE_WATCH_SHARE (IconName): The :code:`device-watch-share` icon. + DEVICE_WATCH_STAR (IconName): The :code:`device-watch-star` icon. + DEVICE_WATCH_STATS (IconName): The :code:`device-watch-stats` icon. + DEVICE_WATCH_STATS_2 (IconName): The :code:`device-watch-stats-2` icon. + DEVICE_WATCH_UP (IconName): The :code:`device-watch-up` icon. + DEVICE_WATCH_X (IconName): The :code:`device-watch-x` icon. + DEVICES (IconName): The :code:`devices` icon. + DEVICES_2 (IconName): The :code:`devices-2` icon. + DEVICES_BOLT (IconName): The :code:`devices-bolt` icon. + DEVICES_CANCEL (IconName): The :code:`devices-cancel` icon. + DEVICES_CHECK (IconName): The :code:`devices-check` icon. + DEVICES_CODE (IconName): The :code:`devices-code` icon. + DEVICES_COG (IconName): The :code:`devices-cog` icon. + DEVICES_DOLLAR (IconName): The :code:`devices-dollar` icon. + DEVICES_DOWN (IconName): The :code:`devices-down` icon. + DEVICES_EXCLAMATION (IconName): The :code:`devices-exclamation` icon. + DEVICES_HEART (IconName): The :code:`devices-heart` icon. + DEVICES_MINUS (IconName): The :code:`devices-minus` icon. + DEVICES_OFF (IconName): The :code:`devices-off` icon. + DEVICES_PAUSE (IconName): The :code:`devices-pause` icon. + DEVICES_PC (IconName): The :code:`devices-pc` icon. + DEVICES_PC_OFF (IconName): The :code:`devices-pc-off` icon. + DEVICES_PIN (IconName): The :code:`devices-pin` icon. + DEVICES_PLUS (IconName): The :code:`devices-plus` icon. + DEVICES_QUESTION (IconName): The :code:`devices-question` icon. + DEVICES_SEARCH (IconName): The :code:`devices-search` icon. + DEVICES_SHARE (IconName): The :code:`devices-share` icon. + DEVICES_STAR (IconName): The :code:`devices-star` icon. + DEVICES_UP (IconName): The :code:`devices-up` icon. + DEVICES_X (IconName): The :code:`devices-x` icon. + DIABOLO (IconName): The :code:`diabolo` icon. + DIABOLO_OFF (IconName): The :code:`diabolo-off` icon. + DIABOLO_PLUS (IconName): The :code:`diabolo-plus` icon. + DIALPAD (IconName): The :code:`dialpad` icon. + DIALPAD_FILLED (IconName): The :code:`dialpad-filled` icon. + DIALPAD_OFF (IconName): The :code:`dialpad-off` icon. + DIAMOND (IconName): The :code:`diamond` icon. + DIAMOND_FILLED (IconName): The :code:`diamond-filled` icon. + DIAMOND_OFF (IconName): The :code:`diamond-off` icon. + DIAMONDS (IconName): The :code:`diamonds` icon. + DIAMONDS_FILLED (IconName): The :code:`diamonds-filled` icon. + DICE (IconName): The :code:`dice` icon. + DICE_1 (IconName): The :code:`dice-1` icon. + DICE_1_FILLED (IconName): The :code:`dice-1-filled` icon. + DICE_2 (IconName): The :code:`dice-2` icon. + DICE_2_FILLED (IconName): The :code:`dice-2-filled` icon. + DICE_3 (IconName): The :code:`dice-3` icon. + DICE_3_FILLED (IconName): The :code:`dice-3-filled` icon. + DICE_4 (IconName): The :code:`dice-4` icon. + DICE_4_FILLED (IconName): The :code:`dice-4-filled` icon. + DICE_5 (IconName): The :code:`dice-5` icon. + DICE_5_FILLED (IconName): The :code:`dice-5-filled` icon. + DICE_6 (IconName): The :code:`dice-6` icon. + DICE_6_FILLED (IconName): The :code:`dice-6-filled` icon. + DICE_FILLED (IconName): The :code:`dice-filled` icon. + DIMENSIONS (IconName): The :code:`dimensions` icon. + DIRECTION (IconName): The :code:`direction` icon. + DIRECTION_HORIZONTAL (IconName): The :code:`direction-horizontal` icon. + DIRECTION_SIGN (IconName): The :code:`direction-sign` icon. + DIRECTION_SIGN_FILLED (IconName): The :code:`direction-sign-filled` icon. + DIRECTION_SIGN_OFF (IconName): The :code:`direction-sign-off` icon. + DIRECTIONS (IconName): The :code:`directions` icon. + DIRECTIONS_OFF (IconName): The :code:`directions-off` icon. + DISABLED (IconName): The :code:`disabled` icon. + DISABLED_2 (IconName): The :code:`disabled-2` icon. + DISABLED_OFF (IconName): The :code:`disabled-off` icon. + DISC (IconName): The :code:`disc` icon. + DISC_GOLF (IconName): The :code:`disc-golf` icon. + DISC_OFF (IconName): The :code:`disc-off` icon. + DISCOUNT (IconName): The :code:`discount` icon. + DISCOUNT_2 (IconName): The :code:`discount-2` icon. + DISCOUNT_2_OFF (IconName): The :code:`discount-2-off` icon. + DISCOUNT_CHECK (IconName): The :code:`discount-check` icon. + DISCOUNT_CHECK_FILLED (IconName): The :code:`discount-check-filled` icon. + DISCOUNT_OFF (IconName): The :code:`discount-off` icon. + DIVIDE (IconName): The :code:`divide` icon. + DNA (IconName): The :code:`dna` icon. + DNA_2 (IconName): The :code:`dna-2` icon. + DNA_2_OFF (IconName): The :code:`dna-2-off` icon. + DNA_OFF (IconName): The :code:`dna-off` icon. + DOG (IconName): The :code:`dog` icon. + DOG_BOWL (IconName): The :code:`dog-bowl` icon. + DOOR (IconName): The :code:`door` icon. + DOOR_ENTER (IconName): The :code:`door-enter` icon. + DOOR_EXIT (IconName): The :code:`door-exit` icon. + DOOR_OFF (IconName): The :code:`door-off` icon. + DOTS (IconName): The :code:`dots` icon. + DOTS_CIRCLE_HORIZONTAL (IconName): The :code:`dots-circle-horizontal` icon. + DOTS_DIAGONAL (IconName): The :code:`dots-diagonal` icon. + DOTS_DIAGONAL_2 (IconName): The :code:`dots-diagonal-2` icon. + DOTS_VERTICAL (IconName): The :code:`dots-vertical` icon. + DOWNLOAD (IconName): The :code:`download` icon. + DOWNLOAD_OFF (IconName): The :code:`download-off` icon. + DRAG_DROP (IconName): The :code:`drag-drop` icon. + DRAG_DROP_2 (IconName): The :code:`drag-drop-2` icon. + DRONE (IconName): The :code:`drone` icon. + DRONE_OFF (IconName): The :code:`drone-off` icon. + DROP_CIRCLE (IconName): The :code:`drop-circle` icon. + DROPLET (IconName): The :code:`droplet` icon. + DROPLET_BOLT (IconName): The :code:`droplet-bolt` icon. + DROPLET_CANCEL (IconName): The :code:`droplet-cancel` icon. + DROPLET_CHECK (IconName): The :code:`droplet-check` icon. + DROPLET_CODE (IconName): The :code:`droplet-code` icon. + DROPLET_COG (IconName): The :code:`droplet-cog` icon. + DROPLET_DOLLAR (IconName): The :code:`droplet-dollar` icon. + DROPLET_DOWN (IconName): The :code:`droplet-down` icon. + DROPLET_EXCLAMATION (IconName): The :code:`droplet-exclamation` icon. + DROPLET_FILLED (IconName): The :code:`droplet-filled` icon. + DROPLET_FILLED_2 (IconName): The :code:`droplet-filled-2` icon. + DROPLET_HALF (IconName): The :code:`droplet-half` icon. + DROPLET_HALF_2 (IconName): The :code:`droplet-half-2` icon. + DROPLET_HALF_FILLED (IconName): The :code:`droplet-half-filled` icon. + DROPLET_HEART (IconName): The :code:`droplet-heart` icon. + DROPLET_MINUS (IconName): The :code:`droplet-minus` icon. + DROPLET_OFF (IconName): The :code:`droplet-off` icon. + DROPLET_PAUSE (IconName): The :code:`droplet-pause` icon. + DROPLET_PIN (IconName): The :code:`droplet-pin` icon. + DROPLET_PLUS (IconName): The :code:`droplet-plus` icon. + DROPLET_QUESTION (IconName): The :code:`droplet-question` icon. + DROPLET_SEARCH (IconName): The :code:`droplet-search` icon. + DROPLET_SHARE (IconName): The :code:`droplet-share` icon. + DROPLET_STAR (IconName): The :code:`droplet-star` icon. + DROPLET_UP (IconName): The :code:`droplet-up` icon. + DROPLET_X (IconName): The :code:`droplet-x` icon. + DUAL_SCREEN (IconName): The :code:`dual-screen` icon. + E_PASSPORT (IconName): The :code:`e-passport` icon. + EAR (IconName): The :code:`ear` icon. + EAR_OFF (IconName): The :code:`ear-off` icon. + EASE_IN (IconName): The :code:`ease-in` icon. + EASE_IN_CONTROL_POINT (IconName): The :code:`ease-in-control-point` icon. + EASE_IN_OUT (IconName): The :code:`ease-in-out` icon. + EASE_IN_OUT_CONTROL_POINTS (IconName): The :code:`ease-in-out-control-points` icon. + EASE_OUT (IconName): The :code:`ease-out` icon. + EASE_OUT_CONTROL_POINT (IconName): The :code:`ease-out-control-point` icon. + EDIT (IconName): The :code:`edit` icon. + EDIT_CIRCLE (IconName): The :code:`edit-circle` icon. + EDIT_CIRCLE_OFF (IconName): The :code:`edit-circle-off` icon. + EDIT_OFF (IconName): The :code:`edit-off` icon. + EGG (IconName): The :code:`egg` icon. + EGG_CRACKED (IconName): The :code:`egg-cracked` icon. + EGG_FILLED (IconName): The :code:`egg-filled` icon. + EGG_FRIED (IconName): The :code:`egg-fried` icon. + EGG_OFF (IconName): The :code:`egg-off` icon. + EGGS (IconName): The :code:`eggs` icon. + ELEVATOR (IconName): The :code:`elevator` icon. + ELEVATOR_OFF (IconName): The :code:`elevator-off` icon. + EMERGENCY_BED (IconName): The :code:`emergency-bed` icon. + EMPATHIZE (IconName): The :code:`empathize` icon. + EMPATHIZE_OFF (IconName): The :code:`empathize-off` icon. + EMPHASIS (IconName): The :code:`emphasis` icon. + ENGINE (IconName): The :code:`engine` icon. + ENGINE_OFF (IconName): The :code:`engine-off` icon. + EQUAL (IconName): The :code:`equal` icon. + EQUAL_DOUBLE (IconName): The :code:`equal-double` icon. + EQUAL_NOT (IconName): The :code:`equal-not` icon. + ERASER (IconName): The :code:`eraser` icon. + ERASER_OFF (IconName): The :code:`eraser-off` icon. + ERROR_404 (IconName): The :code:`error-404` icon. + ERROR_404_OFF (IconName): The :code:`error-404-off` icon. + EXCHANGE (IconName): The :code:`exchange` icon. + EXCHANGE_OFF (IconName): The :code:`exchange-off` icon. + EXCLAMATION_CIRCLE (IconName): The :code:`exclamation-circle` icon. + EXCLAMATION_MARK (IconName): The :code:`exclamation-mark` icon. + EXCLAMATION_MARK_OFF (IconName): The :code:`exclamation-mark-off` icon. + EXPLICIT (IconName): The :code:`explicit` icon. + EXPLICIT_OFF (IconName): The :code:`explicit-off` icon. + EXPOSURE (IconName): The :code:`exposure` icon. + EXPOSURE_0 (IconName): The :code:`exposure-0` icon. + EXPOSURE_MINUS_1 (IconName): The :code:`exposure-minus-1` icon. + EXPOSURE_MINUS_2 (IconName): The :code:`exposure-minus-2` icon. + EXPOSURE_OFF (IconName): The :code:`exposure-off` icon. + EXPOSURE_PLUS_1 (IconName): The :code:`exposure-plus-1` icon. + EXPOSURE_PLUS_2 (IconName): The :code:`exposure-plus-2` icon. + EXTERNAL_LINK (IconName): The :code:`external-link` icon. + EXTERNAL_LINK_OFF (IconName): The :code:`external-link-off` icon. + EYE (IconName): The :code:`eye` icon. + EYE_CHECK (IconName): The :code:`eye-check` icon. + EYE_CLOSED (IconName): The :code:`eye-closed` icon. + EYE_COG (IconName): The :code:`eye-cog` icon. + EYE_EDIT (IconName): The :code:`eye-edit` icon. + EYE_EXCLAMATION (IconName): The :code:`eye-exclamation` icon. + EYE_FILLED (IconName): The :code:`eye-filled` icon. + EYE_HEART (IconName): The :code:`eye-heart` icon. + EYE_OFF (IconName): The :code:`eye-off` icon. + EYE_TABLE (IconName): The :code:`eye-table` icon. + EYE_X (IconName): The :code:`eye-x` icon. + EYEGLASS (IconName): The :code:`eyeglass` icon. + EYEGLASS_2 (IconName): The :code:`eyeglass-2` icon. + EYEGLASS_OFF (IconName): The :code:`eyeglass-off` icon. + FACE_ID (IconName): The :code:`face-id` icon. + FACE_ID_ERROR (IconName): The :code:`face-id-error` icon. + FACE_MASK (IconName): The :code:`face-mask` icon. + FACE_MASK_OFF (IconName): The :code:`face-mask-off` icon. + FALL (IconName): The :code:`fall` icon. + FEATHER (IconName): The :code:`feather` icon. + FEATHER_OFF (IconName): The :code:`feather-off` icon. + FENCE (IconName): The :code:`fence` icon. + FENCE_OFF (IconName): The :code:`fence-off` icon. + FIDGET_SPINNER (IconName): The :code:`fidget-spinner` icon. + FILE (IconName): The :code:`file` icon. + FILE_3D (IconName): The :code:`file-3d` icon. + FILE_ALERT (IconName): The :code:`file-alert` icon. + FILE_ANALYTICS (IconName): The :code:`file-analytics` icon. + FILE_ARROW_LEFT (IconName): The :code:`file-arrow-left` icon. + FILE_ARROW_RIGHT (IconName): The :code:`file-arrow-right` icon. + FILE_BARCODE (IconName): The :code:`file-barcode` icon. + FILE_BROKEN (IconName): The :code:`file-broken` icon. + FILE_CERTIFICATE (IconName): The :code:`file-certificate` icon. + FILE_CHART (IconName): The :code:`file-chart` icon. + FILE_CHECK (IconName): The :code:`file-check` icon. + FILE_CODE (IconName): The :code:`file-code` icon. + FILE_CODE_2 (IconName): The :code:`file-code-2` icon. + FILE_CV (IconName): The :code:`file-cv` icon. + FILE_DATABASE (IconName): The :code:`file-database` icon. + FILE_DELTA (IconName): The :code:`file-delta` icon. + FILE_DESCRIPTION (IconName): The :code:`file-description` icon. + FILE_DIFF (IconName): The :code:`file-diff` icon. + FILE_DIGIT (IconName): The :code:`file-digit` icon. + FILE_DISLIKE (IconName): The :code:`file-dislike` icon. + FILE_DOLLAR (IconName): The :code:`file-dollar` icon. + FILE_DOTS (IconName): The :code:`file-dots` icon. + FILE_DOWNLOAD (IconName): The :code:`file-download` icon. + FILE_EURO (IconName): The :code:`file-euro` icon. + FILE_EXPORT (IconName): The :code:`file-export` icon. + FILE_FILLED (IconName): The :code:`file-filled` icon. + FILE_FUNCTION (IconName): The :code:`file-function` icon. + FILE_HORIZONTAL (IconName): The :code:`file-horizontal` icon. + FILE_IMPORT (IconName): The :code:`file-import` icon. + FILE_INFINITY (IconName): The :code:`file-infinity` icon. + FILE_INFO (IconName): The :code:`file-info` icon. + FILE_INVOICE (IconName): The :code:`file-invoice` icon. + FILE_LAMBDA (IconName): The :code:`file-lambda` icon. + FILE_LIKE (IconName): The :code:`file-like` icon. + FILE_MINUS (IconName): The :code:`file-minus` icon. + FILE_MUSIC (IconName): The :code:`file-music` icon. + FILE_OFF (IconName): The :code:`file-off` icon. + FILE_ORIENTATION (IconName): The :code:`file-orientation` icon. + FILE_PENCIL (IconName): The :code:`file-pencil` icon. + FILE_PERCENT (IconName): The :code:`file-percent` icon. + FILE_PHONE (IconName): The :code:`file-phone` icon. + FILE_PLUS (IconName): The :code:`file-plus` icon. + FILE_POWER (IconName): The :code:`file-power` icon. + FILE_REPORT (IconName): The :code:`file-report` icon. + FILE_RSS (IconName): The :code:`file-rss` icon. + FILE_SCISSORS (IconName): The :code:`file-scissors` icon. + FILE_SEARCH (IconName): The :code:`file-search` icon. + FILE_SETTINGS (IconName): The :code:`file-settings` icon. + FILE_SHREDDER (IconName): The :code:`file-shredder` icon. + FILE_SIGNAL (IconName): The :code:`file-signal` icon. + FILE_SPREADSHEET (IconName): The :code:`file-spreadsheet` icon. + FILE_STACK (IconName): The :code:`file-stack` icon. + FILE_STAR (IconName): The :code:`file-star` icon. + FILE_SYMLINK (IconName): The :code:`file-symlink` icon. + FILE_TEXT (IconName): The :code:`file-text` icon. + FILE_TEXT_AI (IconName): The :code:`file-text-ai` icon. + FILE_TIME (IconName): The :code:`file-time` icon. + FILE_TYPOGRAPHY (IconName): The :code:`file-typography` icon. + FILE_UNKNOWN (IconName): The :code:`file-unknown` icon. + FILE_UPLOAD (IconName): The :code:`file-upload` icon. + FILE_VECTOR (IconName): The :code:`file-vector` icon. + FILE_X (IconName): The :code:`file-x` icon. + FILE_X_FILLED (IconName): The :code:`file-x-filled` icon. + FILE_ZIP (IconName): The :code:`file-zip` icon. + FILES (IconName): The :code:`files` icon. + FILES_OFF (IconName): The :code:`files-off` icon. + FILTER (IconName): The :code:`filter` icon. + FILTER_COG (IconName): The :code:`filter-cog` icon. + FILTER_DOLLAR (IconName): The :code:`filter-dollar` icon. + FILTER_EDIT (IconName): The :code:`filter-edit` icon. + FILTER_MINUS (IconName): The :code:`filter-minus` icon. + FILTER_OFF (IconName): The :code:`filter-off` icon. + FILTER_PLUS (IconName): The :code:`filter-plus` icon. + FILTER_STAR (IconName): The :code:`filter-star` icon. + FILTER_X (IconName): The :code:`filter-x` icon. + FILTERS (IconName): The :code:`filters` icon. + FINGERPRINT (IconName): The :code:`fingerprint` icon. + FINGERPRINT_OFF (IconName): The :code:`fingerprint-off` icon. + FIRE_EXTINGUISHER (IconName): The :code:`fire-extinguisher` icon. + FIRE_HYDRANT (IconName): The :code:`fire-hydrant` icon. + FIRE_HYDRANT_OFF (IconName): The :code:`fire-hydrant-off` icon. + FIRETRUCK (IconName): The :code:`firetruck` icon. + FIRST_AID_KIT (IconName): The :code:`first-aid-kit` icon. + FIRST_AID_KIT_OFF (IconName): The :code:`first-aid-kit-off` icon. + FISH (IconName): The :code:`fish` icon. + FISH_BONE (IconName): The :code:`fish-bone` icon. + FISH_CHRISTIANITY (IconName): The :code:`fish-christianity` icon. + FISH_HOOK (IconName): The :code:`fish-hook` icon. + FISH_HOOK_OFF (IconName): The :code:`fish-hook-off` icon. + FISH_OFF (IconName): The :code:`fish-off` icon. + FLAG (IconName): The :code:`flag` icon. + FLAG_2 (IconName): The :code:`flag-2` icon. + FLAG_2_FILLED (IconName): The :code:`flag-2-filled` icon. + FLAG_2_OFF (IconName): The :code:`flag-2-off` icon. + FLAG_3 (IconName): The :code:`flag-3` icon. + FLAG_3_FILLED (IconName): The :code:`flag-3-filled` icon. + FLAG_FILLED (IconName): The :code:`flag-filled` icon. + FLAG_OFF (IconName): The :code:`flag-off` icon. + FLAME (IconName): The :code:`flame` icon. + FLAME_OFF (IconName): The :code:`flame-off` icon. + FLARE (IconName): The :code:`flare` icon. + FLASK (IconName): The :code:`flask` icon. + FLASK_2 (IconName): The :code:`flask-2` icon. + FLASK_2_OFF (IconName): The :code:`flask-2-off` icon. + FLASK_OFF (IconName): The :code:`flask-off` icon. + FLIP_FLOPS (IconName): The :code:`flip-flops` icon. + FLIP_HORIZONTAL (IconName): The :code:`flip-horizontal` icon. + FLIP_VERTICAL (IconName): The :code:`flip-vertical` icon. + FLOAT_CENTER (IconName): The :code:`float-center` icon. + FLOAT_LEFT (IconName): The :code:`float-left` icon. + FLOAT_NONE (IconName): The :code:`float-none` icon. + FLOAT_RIGHT (IconName): The :code:`float-right` icon. + FLOWER (IconName): The :code:`flower` icon. + FLOWER_OFF (IconName): The :code:`flower-off` icon. + FOCUS (IconName): The :code:`focus` icon. + FOCUS_2 (IconName): The :code:`focus-2` icon. + FOCUS_AUTO (IconName): The :code:`focus-auto` icon. + FOCUS_CENTERED (IconName): The :code:`focus-centered` icon. + FOLD (IconName): The :code:`fold` icon. + FOLD_DOWN (IconName): The :code:`fold-down` icon. + FOLD_UP (IconName): The :code:`fold-up` icon. + FOLDER (IconName): The :code:`folder` icon. + FOLDER_BOLT (IconName): The :code:`folder-bolt` icon. + FOLDER_CANCEL (IconName): The :code:`folder-cancel` icon. + FOLDER_CHECK (IconName): The :code:`folder-check` icon. + FOLDER_CODE (IconName): The :code:`folder-code` icon. + FOLDER_COG (IconName): The :code:`folder-cog` icon. + FOLDER_DOLLAR (IconName): The :code:`folder-dollar` icon. + FOLDER_DOWN (IconName): The :code:`folder-down` icon. + FOLDER_EXCLAMATION (IconName): The :code:`folder-exclamation` icon. + FOLDER_FILLED (IconName): The :code:`folder-filled` icon. + FOLDER_HEART (IconName): The :code:`folder-heart` icon. + FOLDER_MINUS (IconName): The :code:`folder-minus` icon. + FOLDER_OFF (IconName): The :code:`folder-off` icon. + FOLDER_OPEN (IconName): The :code:`folder-open` icon. + FOLDER_PAUSE (IconName): The :code:`folder-pause` icon. + FOLDER_PIN (IconName): The :code:`folder-pin` icon. + FOLDER_PLUS (IconName): The :code:`folder-plus` icon. + FOLDER_QUESTION (IconName): The :code:`folder-question` icon. + FOLDER_SEARCH (IconName): The :code:`folder-search` icon. + FOLDER_SHARE (IconName): The :code:`folder-share` icon. + FOLDER_STAR (IconName): The :code:`folder-star` icon. + FOLDER_SYMLINK (IconName): The :code:`folder-symlink` icon. + FOLDER_UP (IconName): The :code:`folder-up` icon. + FOLDER_X (IconName): The :code:`folder-x` icon. + FOLDERS (IconName): The :code:`folders` icon. + FOLDERS_OFF (IconName): The :code:`folders-off` icon. + FORBID (IconName): The :code:`forbid` icon. + FORBID_2 (IconName): The :code:`forbid-2` icon. + FORKLIFT (IconName): The :code:`forklift` icon. + FORMS (IconName): The :code:`forms` icon. + FOUNTAIN (IconName): The :code:`fountain` icon. + FOUNTAIN_OFF (IconName): The :code:`fountain-off` icon. + FRAME (IconName): The :code:`frame` icon. + FRAME_OFF (IconName): The :code:`frame-off` icon. + FREE_RIGHTS (IconName): The :code:`free-rights` icon. + FREEZE_COLUMN (IconName): The :code:`freeze-column` icon. + FREEZE_ROW (IconName): The :code:`freeze-row` icon. + FREEZE_ROW_COLUMN (IconName): The :code:`freeze-row-column` icon. + FRIDGE (IconName): The :code:`fridge` icon. + FRIDGE_OFF (IconName): The :code:`fridge-off` icon. + FRIENDS (IconName): The :code:`friends` icon. + FRIENDS_OFF (IconName): The :code:`friends-off` icon. + FRUSTUM (IconName): The :code:`frustum` icon. + FRUSTUM_OFF (IconName): The :code:`frustum-off` icon. + FRUSTUM_PLUS (IconName): The :code:`frustum-plus` icon. + FUNCTION (IconName): The :code:`function` icon. + FUNCTION_OFF (IconName): The :code:`function-off` icon. + GARDEN_CART (IconName): The :code:`garden-cart` icon. + GARDEN_CART_OFF (IconName): The :code:`garden-cart-off` icon. + GAS_STATION (IconName): The :code:`gas-station` icon. + GAS_STATION_OFF (IconName): The :code:`gas-station-off` icon. + GAUGE (IconName): The :code:`gauge` icon. + GAUGE_OFF (IconName): The :code:`gauge-off` icon. + GAVEL (IconName): The :code:`gavel` icon. + GENDER_AGENDER (IconName): The :code:`gender-agender` icon. + GENDER_ANDROGYNE (IconName): The :code:`gender-androgyne` icon. + GENDER_BIGENDER (IconName): The :code:`gender-bigender` icon. + GENDER_DEMIBOY (IconName): The :code:`gender-demiboy` icon. + GENDER_DEMIGIRL (IconName): The :code:`gender-demigirl` icon. + GENDER_EPICENE (IconName): The :code:`gender-epicene` icon. + GENDER_FEMALE (IconName): The :code:`gender-female` icon. + GENDER_FEMME (IconName): The :code:`gender-femme` icon. + GENDER_GENDERFLUID (IconName): The :code:`gender-genderfluid` icon. + GENDER_GENDERLESS (IconName): The :code:`gender-genderless` icon. + GENDER_GENDERQUEER (IconName): The :code:`gender-genderqueer` icon. + GENDER_HERMAPHRODITE (IconName): The :code:`gender-hermaphrodite` icon. + GENDER_INTERGENDER (IconName): The :code:`gender-intergender` icon. + GENDER_MALE (IconName): The :code:`gender-male` icon. + GENDER_NEUTROIS (IconName): The :code:`gender-neutrois` icon. + GENDER_THIRD (IconName): The :code:`gender-third` icon. + GENDER_TRANSGENDER (IconName): The :code:`gender-transgender` icon. + GENDER_TRASVESTI (IconName): The :code:`gender-trasvesti` icon. + GEOMETRY (IconName): The :code:`geometry` icon. + GHOST (IconName): The :code:`ghost` icon. + GHOST_2 (IconName): The :code:`ghost-2` icon. + GHOST_2_FILLED (IconName): The :code:`ghost-2-filled` icon. + GHOST_FILLED (IconName): The :code:`ghost-filled` icon. + GHOST_OFF (IconName): The :code:`ghost-off` icon. + GIF (IconName): The :code:`gif` icon. + GIFT (IconName): The :code:`gift` icon. + GIFT_CARD (IconName): The :code:`gift-card` icon. + GIFT_OFF (IconName): The :code:`gift-off` icon. + GIT_BRANCH (IconName): The :code:`git-branch` icon. + GIT_BRANCH_DELETED (IconName): The :code:`git-branch-deleted` icon. + GIT_CHERRY_PICK (IconName): The :code:`git-cherry-pick` icon. + GIT_COMMIT (IconName): The :code:`git-commit` icon. + GIT_COMPARE (IconName): The :code:`git-compare` icon. + GIT_FORK (IconName): The :code:`git-fork` icon. + GIT_MERGE (IconName): The :code:`git-merge` icon. + GIT_PULL_REQUEST (IconName): The :code:`git-pull-request` icon. + GIT_PULL_REQUEST_CLOSED (IconName): The :code:`git-pull-request-closed` icon. + GIT_PULL_REQUEST_DRAFT (IconName): The :code:`git-pull-request-draft` icon. + GIZMO (IconName): The :code:`gizmo` icon. + GLASS (IconName): The :code:`glass` icon. + GLASS_FULL (IconName): The :code:`glass-full` icon. + GLASS_OFF (IconName): The :code:`glass-off` icon. + GLOBE (IconName): The :code:`globe` icon. + GLOBE_OFF (IconName): The :code:`globe-off` icon. + GO_GAME (IconName): The :code:`go-game` icon. + GOLF (IconName): The :code:`golf` icon. + GOLF_OFF (IconName): The :code:`golf-off` icon. + GPS (IconName): The :code:`gps` icon. + GRADIENTER (IconName): The :code:`gradienter` icon. + GRAIN (IconName): The :code:`grain` icon. + GRAPH (IconName): The :code:`graph` icon. + GRAPH_OFF (IconName): The :code:`graph-off` icon. + GRAVE (IconName): The :code:`grave` icon. + GRAVE_2 (IconName): The :code:`grave-2` icon. + GRID_DOTS (IconName): The :code:`grid-dots` icon. + GRID_PATTERN (IconName): The :code:`grid-pattern` icon. + GRILL (IconName): The :code:`grill` icon. + GRILL_FORK (IconName): The :code:`grill-fork` icon. + GRILL_OFF (IconName): The :code:`grill-off` icon. + GRILL_SPATULA (IconName): The :code:`grill-spatula` icon. + GRIP_HORIZONTAL (IconName): The :code:`grip-horizontal` icon. + GRIP_VERTICAL (IconName): The :code:`grip-vertical` icon. + GROWTH (IconName): The :code:`growth` icon. + GUITAR_PICK (IconName): The :code:`guitar-pick` icon. + GUITAR_PICK_FILLED (IconName): The :code:`guitar-pick-filled` icon. + H_1 (IconName): The :code:`h-1` icon. + H_2 (IconName): The :code:`h-2` icon. + H_3 (IconName): The :code:`h-3` icon. + H_4 (IconName): The :code:`h-4` icon. + H_5 (IconName): The :code:`h-5` icon. + H_6 (IconName): The :code:`h-6` icon. + HAMMER (IconName): The :code:`hammer` icon. + HAMMER_OFF (IconName): The :code:`hammer-off` icon. + HAND_CLICK (IconName): The :code:`hand-click` icon. + HAND_FINGER (IconName): The :code:`hand-finger` icon. + HAND_FINGER_OFF (IconName): The :code:`hand-finger-off` icon. + HAND_GRAB (IconName): The :code:`hand-grab` icon. + HAND_LITTLE_FINGER (IconName): The :code:`hand-little-finger` icon. + HAND_MIDDLE_FINGER (IconName): The :code:`hand-middle-finger` icon. + HAND_MOVE (IconName): The :code:`hand-move` icon. + HAND_OFF (IconName): The :code:`hand-off` icon. + HAND_RING_FINGER (IconName): The :code:`hand-ring-finger` icon. + HAND_ROCK (IconName): The :code:`hand-rock` icon. + HAND_SANITIZER (IconName): The :code:`hand-sanitizer` icon. + HAND_STOP (IconName): The :code:`hand-stop` icon. + HAND_THREE_FINGERS (IconName): The :code:`hand-three-fingers` icon. + HAND_TWO_FINGERS (IconName): The :code:`hand-two-fingers` icon. + HANGER (IconName): The :code:`hanger` icon. + HANGER_2 (IconName): The :code:`hanger-2` icon. + HANGER_OFF (IconName): The :code:`hanger-off` icon. + HASH (IconName): The :code:`hash` icon. + HAZE (IconName): The :code:`haze` icon. + HAZE_MOON (IconName): The :code:`haze-moon` icon. + HDR (IconName): The :code:`hdr` icon. + HEADING (IconName): The :code:`heading` icon. + HEADING_OFF (IconName): The :code:`heading-off` icon. + HEADPHONES (IconName): The :code:`headphones` icon. + HEADPHONES_FILLED (IconName): The :code:`headphones-filled` icon. + HEADPHONES_OFF (IconName): The :code:`headphones-off` icon. + HEADSET (IconName): The :code:`headset` icon. + HEADSET_OFF (IconName): The :code:`headset-off` icon. + HEALTH_RECOGNITION (IconName): The :code:`health-recognition` icon. + HEART (IconName): The :code:`heart` icon. + HEART_BROKEN (IconName): The :code:`heart-broken` icon. + HEART_FILLED (IconName): The :code:`heart-filled` icon. + HEART_HANDSHAKE (IconName): The :code:`heart-handshake` icon. + HEART_MINUS (IconName): The :code:`heart-minus` icon. + HEART_OFF (IconName): The :code:`heart-off` icon. + HEART_PLUS (IconName): The :code:`heart-plus` icon. + HEART_RATE_MONITOR (IconName): The :code:`heart-rate-monitor` icon. + HEARTBEAT (IconName): The :code:`heartbeat` icon. + HEARTS (IconName): The :code:`hearts` icon. + HEARTS_OFF (IconName): The :code:`hearts-off` icon. + HELICOPTER (IconName): The :code:`helicopter` icon. + HELICOPTER_LANDING (IconName): The :code:`helicopter-landing` icon. + HELMET (IconName): The :code:`helmet` icon. + HELMET_OFF (IconName): The :code:`helmet-off` icon. + HELP (IconName): The :code:`help` icon. + HELP_CIRCLE (IconName): The :code:`help-circle` icon. + HELP_CIRCLE_FILLED (IconName): The :code:`help-circle-filled` icon. + HELP_HEXAGON (IconName): The :code:`help-hexagon` icon. + HELP_HEXAGON_FILLED (IconName): The :code:`help-hexagon-filled` icon. + HELP_OCTAGON (IconName): The :code:`help-octagon` icon. + HELP_OCTAGON_FILLED (IconName): The :code:`help-octagon-filled` icon. + HELP_OFF (IconName): The :code:`help-off` icon. + HELP_SMALL (IconName): The :code:`help-small` icon. + HELP_SQUARE (IconName): The :code:`help-square` icon. + HELP_SQUARE_FILLED (IconName): The :code:`help-square-filled` icon. + HELP_SQUARE_ROUNDED (IconName): The :code:`help-square-rounded` icon. + HELP_SQUARE_ROUNDED_FILLED (IconName): The :code:`help-square-rounded-filled` icon. + HELP_TRIANGLE (IconName): The :code:`help-triangle` icon. + HELP_TRIANGLE_FILLED (IconName): The :code:`help-triangle-filled` icon. + HEMISPHERE (IconName): The :code:`hemisphere` icon. + HEMISPHERE_OFF (IconName): The :code:`hemisphere-off` icon. + HEMISPHERE_PLUS (IconName): The :code:`hemisphere-plus` icon. + HEXAGON (IconName): The :code:`hexagon` icon. + HEXAGON_0_FILLED (IconName): The :code:`hexagon-0-filled` icon. + HEXAGON_1_FILLED (IconName): The :code:`hexagon-1-filled` icon. + HEXAGON_2_FILLED (IconName): The :code:`hexagon-2-filled` icon. + HEXAGON_3_FILLED (IconName): The :code:`hexagon-3-filled` icon. + HEXAGON_3D (IconName): The :code:`hexagon-3d` icon. + HEXAGON_4_FILLED (IconName): The :code:`hexagon-4-filled` icon. + HEXAGON_5_FILLED (IconName): The :code:`hexagon-5-filled` icon. + HEXAGON_6_FILLED (IconName): The :code:`hexagon-6-filled` icon. + HEXAGON_7_FILLED (IconName): The :code:`hexagon-7-filled` icon. + HEXAGON_8_FILLED (IconName): The :code:`hexagon-8-filled` icon. + HEXAGON_9_FILLED (IconName): The :code:`hexagon-9-filled` icon. + HEXAGON_FILLED (IconName): The :code:`hexagon-filled` icon. + HEXAGON_LETTER_A (IconName): The :code:`hexagon-letter-a` icon. + HEXAGON_LETTER_B (IconName): The :code:`hexagon-letter-b` icon. + HEXAGON_LETTER_C (IconName): The :code:`hexagon-letter-c` icon. + HEXAGON_LETTER_D (IconName): The :code:`hexagon-letter-d` icon. + HEXAGON_LETTER_E (IconName): The :code:`hexagon-letter-e` icon. + HEXAGON_LETTER_F (IconName): The :code:`hexagon-letter-f` icon. + HEXAGON_LETTER_G (IconName): The :code:`hexagon-letter-g` icon. + HEXAGON_LETTER_H (IconName): The :code:`hexagon-letter-h` icon. + HEXAGON_LETTER_I (IconName): The :code:`hexagon-letter-i` icon. + HEXAGON_LETTER_J (IconName): The :code:`hexagon-letter-j` icon. + HEXAGON_LETTER_K (IconName): The :code:`hexagon-letter-k` icon. + HEXAGON_LETTER_L (IconName): The :code:`hexagon-letter-l` icon. + HEXAGON_LETTER_M (IconName): The :code:`hexagon-letter-m` icon. + HEXAGON_LETTER_N (IconName): The :code:`hexagon-letter-n` icon. + HEXAGON_LETTER_O (IconName): The :code:`hexagon-letter-o` icon. + HEXAGON_LETTER_P (IconName): The :code:`hexagon-letter-p` icon. + HEXAGON_LETTER_Q (IconName): The :code:`hexagon-letter-q` icon. + HEXAGON_LETTER_R (IconName): The :code:`hexagon-letter-r` icon. + HEXAGON_LETTER_S (IconName): The :code:`hexagon-letter-s` icon. + HEXAGON_LETTER_T (IconName): The :code:`hexagon-letter-t` icon. + HEXAGON_LETTER_U (IconName): The :code:`hexagon-letter-u` icon. + HEXAGON_LETTER_V (IconName): The :code:`hexagon-letter-v` icon. + HEXAGON_LETTER_W (IconName): The :code:`hexagon-letter-w` icon. + HEXAGON_LETTER_X (IconName): The :code:`hexagon-letter-x` icon. + HEXAGON_LETTER_Y (IconName): The :code:`hexagon-letter-y` icon. + HEXAGON_LETTER_Z (IconName): The :code:`hexagon-letter-z` icon. + HEXAGON_NUMBER_0 (IconName): The :code:`hexagon-number-0` icon. + HEXAGON_NUMBER_1 (IconName): The :code:`hexagon-number-1` icon. + HEXAGON_NUMBER_2 (IconName): The :code:`hexagon-number-2` icon. + HEXAGON_NUMBER_3 (IconName): The :code:`hexagon-number-3` icon. + HEXAGON_NUMBER_4 (IconName): The :code:`hexagon-number-4` icon. + HEXAGON_NUMBER_5 (IconName): The :code:`hexagon-number-5` icon. + HEXAGON_NUMBER_6 (IconName): The :code:`hexagon-number-6` icon. + HEXAGON_NUMBER_7 (IconName): The :code:`hexagon-number-7` icon. + HEXAGON_NUMBER_8 (IconName): The :code:`hexagon-number-8` icon. + HEXAGON_NUMBER_9 (IconName): The :code:`hexagon-number-9` icon. + HEXAGON_OFF (IconName): The :code:`hexagon-off` icon. + HEXAGONAL_PRISM (IconName): The :code:`hexagonal-prism` icon. + HEXAGONAL_PRISM_OFF (IconName): The :code:`hexagonal-prism-off` icon. + HEXAGONAL_PRISM_PLUS (IconName): The :code:`hexagonal-prism-plus` icon. + HEXAGONAL_PYRAMID (IconName): The :code:`hexagonal-pyramid` icon. + HEXAGONAL_PYRAMID_OFF (IconName): The :code:`hexagonal-pyramid-off` icon. + HEXAGONAL_PYRAMID_PLUS (IconName): The :code:`hexagonal-pyramid-plus` icon. + HEXAGONS (IconName): The :code:`hexagons` icon. + HEXAGONS_OFF (IconName): The :code:`hexagons-off` icon. + HIERARCHY (IconName): The :code:`hierarchy` icon. + HIERARCHY_2 (IconName): The :code:`hierarchy-2` icon. + HIERARCHY_3 (IconName): The :code:`hierarchy-3` icon. + HIERARCHY_OFF (IconName): The :code:`hierarchy-off` icon. + HIGHLIGHT (IconName): The :code:`highlight` icon. + HIGHLIGHT_OFF (IconName): The :code:`highlight-off` icon. + HISTORY (IconName): The :code:`history` icon. + HISTORY_OFF (IconName): The :code:`history-off` icon. + HISTORY_TOGGLE (IconName): The :code:`history-toggle` icon. + HOME (IconName): The :code:`home` icon. + HOME_2 (IconName): The :code:`home-2` icon. + HOME_BOLT (IconName): The :code:`home-bolt` icon. + HOME_CANCEL (IconName): The :code:`home-cancel` icon. + HOME_CHECK (IconName): The :code:`home-check` icon. + HOME_COG (IconName): The :code:`home-cog` icon. + HOME_DOLLAR (IconName): The :code:`home-dollar` icon. + HOME_DOT (IconName): The :code:`home-dot` icon. + HOME_DOWN (IconName): The :code:`home-down` icon. + HOME_ECO (IconName): The :code:`home-eco` icon. + HOME_EDIT (IconName): The :code:`home-edit` icon. + HOME_EXCLAMATION (IconName): The :code:`home-exclamation` icon. + HOME_HAND (IconName): The :code:`home-hand` icon. + HOME_HEART (IconName): The :code:`home-heart` icon. + HOME_INFINITY (IconName): The :code:`home-infinity` icon. + HOME_LINK (IconName): The :code:`home-link` icon. + HOME_MINUS (IconName): The :code:`home-minus` icon. + HOME_MOVE (IconName): The :code:`home-move` icon. + HOME_OFF (IconName): The :code:`home-off` icon. + HOME_PLUS (IconName): The :code:`home-plus` icon. + HOME_QUESTION (IconName): The :code:`home-question` icon. + HOME_RIBBON (IconName): The :code:`home-ribbon` icon. + HOME_SEARCH (IconName): The :code:`home-search` icon. + HOME_SHARE (IconName): The :code:`home-share` icon. + HOME_SHIELD (IconName): The :code:`home-shield` icon. + HOME_SIGNAL (IconName): The :code:`home-signal` icon. + HOME_STAR (IconName): The :code:`home-star` icon. + HOME_STATS (IconName): The :code:`home-stats` icon. + HOME_UP (IconName): The :code:`home-up` icon. + HOME_X (IconName): The :code:`home-x` icon. + HORSE_TOY (IconName): The :code:`horse-toy` icon. + HOTEL_SERVICE (IconName): The :code:`hotel-service` icon. + HOURGLASS (IconName): The :code:`hourglass` icon. + HOURGLASS_EMPTY (IconName): The :code:`hourglass-empty` icon. + HOURGLASS_FILLED (IconName): The :code:`hourglass-filled` icon. + HOURGLASS_HIGH (IconName): The :code:`hourglass-high` icon. + HOURGLASS_LOW (IconName): The :code:`hourglass-low` icon. + HOURGLASS_OFF (IconName): The :code:`hourglass-off` icon. + HTML (IconName): The :code:`html` icon. + HTTP_CONNECT (IconName): The :code:`http-connect` icon. + HTTP_DELETE (IconName): The :code:`http-delete` icon. + HTTP_GET (IconName): The :code:`http-get` icon. + HTTP_HEAD (IconName): The :code:`http-head` icon. + HTTP_OPTIONS (IconName): The :code:`http-options` icon. + HTTP_PATCH (IconName): The :code:`http-patch` icon. + HTTP_POST (IconName): The :code:`http-post` icon. + HTTP_PUT (IconName): The :code:`http-put` icon. + HTTP_QUE (IconName): The :code:`http-que` icon. + HTTP_TRACE (IconName): The :code:`http-trace` icon. + ICE_CREAM (IconName): The :code:`ice-cream` icon. + ICE_CREAM_2 (IconName): The :code:`ice-cream-2` icon. + ICE_CREAM_OFF (IconName): The :code:`ice-cream-off` icon. + ICE_SKATING (IconName): The :code:`ice-skating` icon. + ICONS (IconName): The :code:`icons` icon. + ICONS_OFF (IconName): The :code:`icons-off` icon. + ID (IconName): The :code:`id` icon. + ID_BADGE (IconName): The :code:`id-badge` icon. + ID_BADGE_2 (IconName): The :code:`id-badge-2` icon. + ID_BADGE_OFF (IconName): The :code:`id-badge-off` icon. + ID_OFF (IconName): The :code:`id-off` icon. + INBOX (IconName): The :code:`inbox` icon. + INBOX_OFF (IconName): The :code:`inbox-off` icon. + INDENT_DECREASE (IconName): The :code:`indent-decrease` icon. + INDENT_INCREASE (IconName): The :code:`indent-increase` icon. + INFINITY (IconName): The :code:`infinity` icon. + INFINITY_OFF (IconName): The :code:`infinity-off` icon. + INFO_CIRCLE (IconName): The :code:`info-circle` icon. + INFO_CIRCLE_FILLED (IconName): The :code:`info-circle-filled` icon. + INFO_HEXAGON (IconName): The :code:`info-hexagon` icon. + INFO_HEXAGON_FILLED (IconName): The :code:`info-hexagon-filled` icon. + INFO_OCTAGON (IconName): The :code:`info-octagon` icon. + INFO_OCTAGON_FILLED (IconName): The :code:`info-octagon-filled` icon. + INFO_SMALL (IconName): The :code:`info-small` icon. + INFO_SQUARE (IconName): The :code:`info-square` icon. + INFO_SQUARE_FILLED (IconName): The :code:`info-square-filled` icon. + INFO_SQUARE_ROUNDED (IconName): The :code:`info-square-rounded` icon. + INFO_SQUARE_ROUNDED_FILLED (IconName): The :code:`info-square-rounded-filled` icon. + INFO_TRIANGLE (IconName): The :code:`info-triangle` icon. + INFO_TRIANGLE_FILLED (IconName): The :code:`info-triangle-filled` icon. + INNER_SHADOW_BOTTOM (IconName): The :code:`inner-shadow-bottom` icon. + INNER_SHADOW_BOTTOM_FILLED (IconName): The :code:`inner-shadow-bottom-filled` icon. + INNER_SHADOW_BOTTOM_LEFT (IconName): The :code:`inner-shadow-bottom-left` icon. + INNER_SHADOW_BOTTOM_LEFT_FILLED (IconName): The :code:`inner-shadow-bottom-left-filled` icon. + INNER_SHADOW_BOTTOM_RIGHT (IconName): The :code:`inner-shadow-bottom-right` icon. + INNER_SHADOW_BOTTOM_RIGHT_FILLED (IconName): The :code:`inner-shadow-bottom-right-filled` icon. + INNER_SHADOW_LEFT (IconName): The :code:`inner-shadow-left` icon. + INNER_SHADOW_LEFT_FILLED (IconName): The :code:`inner-shadow-left-filled` icon. + INNER_SHADOW_RIGHT (IconName): The :code:`inner-shadow-right` icon. + INNER_SHADOW_RIGHT_FILLED (IconName): The :code:`inner-shadow-right-filled` icon. + INNER_SHADOW_TOP (IconName): The :code:`inner-shadow-top` icon. + INNER_SHADOW_TOP_FILLED (IconName): The :code:`inner-shadow-top-filled` icon. + INNER_SHADOW_TOP_LEFT (IconName): The :code:`inner-shadow-top-left` icon. + INNER_SHADOW_TOP_LEFT_FILLED (IconName): The :code:`inner-shadow-top-left-filled` icon. + INNER_SHADOW_TOP_RIGHT (IconName): The :code:`inner-shadow-top-right` icon. + INNER_SHADOW_TOP_RIGHT_FILLED (IconName): The :code:`inner-shadow-top-right-filled` icon. + INPUT_SEARCH (IconName): The :code:`input-search` icon. + IRONING (IconName): The :code:`ironing` icon. + IRONING_1 (IconName): The :code:`ironing-1` icon. + IRONING_2 (IconName): The :code:`ironing-2` icon. + IRONING_3 (IconName): The :code:`ironing-3` icon. + IRONING_OFF (IconName): The :code:`ironing-off` icon. + IRONING_STEAM (IconName): The :code:`ironing-steam` icon. + IRONING_STEAM_OFF (IconName): The :code:`ironing-steam-off` icon. + IRREGULAR_POLYHEDRON (IconName): The :code:`irregular-polyhedron` icon. + IRREGULAR_POLYHEDRON_OFF (IconName): The :code:`irregular-polyhedron-off` icon. + IRREGULAR_POLYHEDRON_PLUS (IconName): The :code:`irregular-polyhedron-plus` icon. + ITALIC (IconName): The :code:`italic` icon. + JACKET (IconName): The :code:`jacket` icon. + JETPACK (IconName): The :code:`jetpack` icon. + JEWISH_STAR (IconName): The :code:`jewish-star` icon. + JEWISH_STAR_FILLED (IconName): The :code:`jewish-star-filled` icon. + JPG (IconName): The :code:`jpg` icon. + JSON (IconName): The :code:`json` icon. + JUMP_ROPE (IconName): The :code:`jump-rope` icon. + KARATE (IconName): The :code:`karate` icon. + KAYAK (IconName): The :code:`kayak` icon. + KERING (IconName): The :code:`kering` icon. + KEY (IconName): The :code:`key` icon. + KEY_OFF (IconName): The :code:`key-off` icon. + KEYBOARD (IconName): The :code:`keyboard` icon. + KEYBOARD_HIDE (IconName): The :code:`keyboard-hide` icon. + KEYBOARD_OFF (IconName): The :code:`keyboard-off` icon. + KEYBOARD_SHOW (IconName): The :code:`keyboard-show` icon. + KEYFRAME (IconName): The :code:`keyframe` icon. + KEYFRAME_ALIGN_CENTER (IconName): The :code:`keyframe-align-center` icon. + KEYFRAME_ALIGN_HORIZONTAL (IconName): The :code:`keyframe-align-horizontal` icon. + KEYFRAME_ALIGN_VERTICAL (IconName): The :code:`keyframe-align-vertical` icon. + KEYFRAMES (IconName): The :code:`keyframes` icon. + LADDER (IconName): The :code:`ladder` icon. + LADDER_OFF (IconName): The :code:`ladder-off` icon. + LAMBDA (IconName): The :code:`lambda` icon. + LAMP (IconName): The :code:`lamp` icon. + LAMP_2 (IconName): The :code:`lamp-2` icon. + LAMP_OFF (IconName): The :code:`lamp-off` icon. + LANE (IconName): The :code:`lane` icon. + LANGUAGE (IconName): The :code:`language` icon. + LANGUAGE_HIRAGANA (IconName): The :code:`language-hiragana` icon. + LANGUAGE_KATAKANA (IconName): The :code:`language-katakana` icon. + LANGUAGE_OFF (IconName): The :code:`language-off` icon. + LASSO (IconName): The :code:`lasso` icon. + LASSO_OFF (IconName): The :code:`lasso-off` icon. + LASSO_POLYGON (IconName): The :code:`lasso-polygon` icon. + LAYERS_DIFFERENCE (IconName): The :code:`layers-difference` icon. + LAYERS_INTERSECT (IconName): The :code:`layers-intersect` icon. + LAYERS_INTERSECT_2 (IconName): The :code:`layers-intersect-2` icon. + LAYERS_LINKED (IconName): The :code:`layers-linked` icon. + LAYERS_OFF (IconName): The :code:`layers-off` icon. + LAYERS_SUBTRACT (IconName): The :code:`layers-subtract` icon. + LAYERS_UNION (IconName): The :code:`layers-union` icon. + LAYOUT (IconName): The :code:`layout` icon. + LAYOUT_2 (IconName): The :code:`layout-2` icon. + LAYOUT_ALIGN_BOTTOM (IconName): The :code:`layout-align-bottom` icon. + LAYOUT_ALIGN_CENTER (IconName): The :code:`layout-align-center` icon. + LAYOUT_ALIGN_LEFT (IconName): The :code:`layout-align-left` icon. + LAYOUT_ALIGN_MIDDLE (IconName): The :code:`layout-align-middle` icon. + LAYOUT_ALIGN_RIGHT (IconName): The :code:`layout-align-right` icon. + LAYOUT_ALIGN_TOP (IconName): The :code:`layout-align-top` icon. + LAYOUT_BOARD (IconName): The :code:`layout-board` icon. + LAYOUT_BOARD_SPLIT (IconName): The :code:`layout-board-split` icon. + LAYOUT_BOTTOMBAR (IconName): The :code:`layout-bottombar` icon. + LAYOUT_BOTTOMBAR_COLLAPSE (IconName): The :code:`layout-bottombar-collapse` icon. + LAYOUT_BOTTOMBAR_EXPAND (IconName): The :code:`layout-bottombar-expand` icon. + LAYOUT_CARDS (IconName): The :code:`layout-cards` icon. + LAYOUT_COLLAGE (IconName): The :code:`layout-collage` icon. + LAYOUT_COLUMNS (IconName): The :code:`layout-columns` icon. + LAYOUT_DASHBOARD (IconName): The :code:`layout-dashboard` icon. + LAYOUT_DISTRIBUTE_HORIZONTAL (IconName): The :code:`layout-distribute-horizontal` icon. + LAYOUT_DISTRIBUTE_VERTICAL (IconName): The :code:`layout-distribute-vertical` icon. + LAYOUT_GRID (IconName): The :code:`layout-grid` icon. + LAYOUT_GRID_ADD (IconName): The :code:`layout-grid-add` icon. + LAYOUT_GRID_REMOVE (IconName): The :code:`layout-grid-remove` icon. + LAYOUT_KANBAN (IconName): The :code:`layout-kanban` icon. + LAYOUT_LIST (IconName): The :code:`layout-list` icon. + LAYOUT_NAVBAR (IconName): The :code:`layout-navbar` icon. + LAYOUT_NAVBAR_COLLAPSE (IconName): The :code:`layout-navbar-collapse` icon. + LAYOUT_NAVBAR_EXPAND (IconName): The :code:`layout-navbar-expand` icon. + LAYOUT_OFF (IconName): The :code:`layout-off` icon. + LAYOUT_ROWS (IconName): The :code:`layout-rows` icon. + LAYOUT_SIDEBAR (IconName): The :code:`layout-sidebar` icon. + LAYOUT_SIDEBAR_LEFT_COLLAPSE (IconName): The :code:`layout-sidebar-left-collapse` icon. + LAYOUT_SIDEBAR_LEFT_EXPAND (IconName): The :code:`layout-sidebar-left-expand` icon. + LAYOUT_SIDEBAR_RIGHT (IconName): The :code:`layout-sidebar-right` icon. + LAYOUT_SIDEBAR_RIGHT_COLLAPSE (IconName): The :code:`layout-sidebar-right-collapse` icon. + LAYOUT_SIDEBAR_RIGHT_EXPAND (IconName): The :code:`layout-sidebar-right-expand` icon. + LEAF (IconName): The :code:`leaf` icon. + LEAF_OFF (IconName): The :code:`leaf-off` icon. + LEGO (IconName): The :code:`lego` icon. + LEGO_OFF (IconName): The :code:`lego-off` icon. + LEMON (IconName): The :code:`lemon` icon. + LEMON_2 (IconName): The :code:`lemon-2` icon. + LETTER_A (IconName): The :code:`letter-a` icon. + LETTER_B (IconName): The :code:`letter-b` icon. + LETTER_C (IconName): The :code:`letter-c` icon. + LETTER_CASE (IconName): The :code:`letter-case` icon. + LETTER_CASE_LOWER (IconName): The :code:`letter-case-lower` icon. + LETTER_CASE_TOGGLE (IconName): The :code:`letter-case-toggle` icon. + LETTER_CASE_UPPER (IconName): The :code:`letter-case-upper` icon. + LETTER_D (IconName): The :code:`letter-d` icon. + LETTER_E (IconName): The :code:`letter-e` icon. + LETTER_F (IconName): The :code:`letter-f` icon. + LETTER_G (IconName): The :code:`letter-g` icon. + LETTER_H (IconName): The :code:`letter-h` icon. + LETTER_I (IconName): The :code:`letter-i` icon. + LETTER_J (IconName): The :code:`letter-j` icon. + LETTER_K (IconName): The :code:`letter-k` icon. + LETTER_L (IconName): The :code:`letter-l` icon. + LETTER_M (IconName): The :code:`letter-m` icon. + LETTER_N (IconName): The :code:`letter-n` icon. + LETTER_O (IconName): The :code:`letter-o` icon. + LETTER_P (IconName): The :code:`letter-p` icon. + LETTER_Q (IconName): The :code:`letter-q` icon. + LETTER_R (IconName): The :code:`letter-r` icon. + LETTER_S (IconName): The :code:`letter-s` icon. + LETTER_SPACING (IconName): The :code:`letter-spacing` icon. + LETTER_T (IconName): The :code:`letter-t` icon. + LETTER_U (IconName): The :code:`letter-u` icon. + LETTER_V (IconName): The :code:`letter-v` icon. + LETTER_W (IconName): The :code:`letter-w` icon. + LETTER_X (IconName): The :code:`letter-x` icon. + LETTER_Y (IconName): The :code:`letter-y` icon. + LETTER_Z (IconName): The :code:`letter-z` icon. + LICENSE (IconName): The :code:`license` icon. + LICENSE_OFF (IconName): The :code:`license-off` icon. + LIFEBUOY (IconName): The :code:`lifebuoy` icon. + LIFEBUOY_OFF (IconName): The :code:`lifebuoy-off` icon. + LIGHTER (IconName): The :code:`lighter` icon. + LINE (IconName): The :code:`line` icon. + LINE_DASHED (IconName): The :code:`line-dashed` icon. + LINE_DOTTED (IconName): The :code:`line-dotted` icon. + LINE_HEIGHT (IconName): The :code:`line-height` icon. + LINK (IconName): The :code:`link` icon. + LINK_OFF (IconName): The :code:`link-off` icon. + LIST (IconName): The :code:`list` icon. + LIST_CHECK (IconName): The :code:`list-check` icon. + LIST_DETAILS (IconName): The :code:`list-details` icon. + LIST_NUMBERS (IconName): The :code:`list-numbers` icon. + LIST_SEARCH (IconName): The :code:`list-search` icon. + LIST_TREE (IconName): The :code:`list-tree` icon. + LIVE_PHOTO (IconName): The :code:`live-photo` icon. + LIVE_PHOTO_OFF (IconName): The :code:`live-photo-off` icon. + LIVE_VIEW (IconName): The :code:`live-view` icon. + LOAD_BALANCER (IconName): The :code:`load-balancer` icon. + LOADER (IconName): The :code:`loader` icon. + LOADER_2 (IconName): The :code:`loader-2` icon. + LOADER_3 (IconName): The :code:`loader-3` icon. + LOADER_QUARTER (IconName): The :code:`loader-quarter` icon. + LOCATION (IconName): The :code:`location` icon. + LOCATION_BROKEN (IconName): The :code:`location-broken` icon. + LOCATION_FILLED (IconName): The :code:`location-filled` icon. + LOCATION_OFF (IconName): The :code:`location-off` icon. + LOCK (IconName): The :code:`lock` icon. + LOCK_ACCESS (IconName): The :code:`lock-access` icon. + LOCK_ACCESS_OFF (IconName): The :code:`lock-access-off` icon. + LOCK_BOLT (IconName): The :code:`lock-bolt` icon. + LOCK_CANCEL (IconName): The :code:`lock-cancel` icon. + LOCK_CHECK (IconName): The :code:`lock-check` icon. + LOCK_CODE (IconName): The :code:`lock-code` icon. + LOCK_COG (IconName): The :code:`lock-cog` icon. + LOCK_DOLLAR (IconName): The :code:`lock-dollar` icon. + LOCK_DOWN (IconName): The :code:`lock-down` icon. + LOCK_EXCLAMATION (IconName): The :code:`lock-exclamation` icon. + LOCK_HEART (IconName): The :code:`lock-heart` icon. + LOCK_MINUS (IconName): The :code:`lock-minus` icon. + LOCK_OFF (IconName): The :code:`lock-off` icon. + LOCK_OPEN (IconName): The :code:`lock-open` icon. + LOCK_OPEN_OFF (IconName): The :code:`lock-open-off` icon. + LOCK_PAUSE (IconName): The :code:`lock-pause` icon. + LOCK_PIN (IconName): The :code:`lock-pin` icon. + LOCK_PLUS (IconName): The :code:`lock-plus` icon. + LOCK_QUESTION (IconName): The :code:`lock-question` icon. + LOCK_SEARCH (IconName): The :code:`lock-search` icon. + LOCK_SHARE (IconName): The :code:`lock-share` icon. + LOCK_SQUARE (IconName): The :code:`lock-square` icon. + LOCK_SQUARE_ROUNDED (IconName): The :code:`lock-square-rounded` icon. + LOCK_SQUARE_ROUNDED_FILLED (IconName): The :code:`lock-square-rounded-filled` icon. + LOCK_STAR (IconName): The :code:`lock-star` icon. + LOCK_UP (IconName): The :code:`lock-up` icon. + LOCK_X (IconName): The :code:`lock-x` icon. + LOGIC_AND (IconName): The :code:`logic-and` icon. + LOGIC_BUFFER (IconName): The :code:`logic-buffer` icon. + LOGIC_NAND (IconName): The :code:`logic-nand` icon. + LOGIC_NOR (IconName): The :code:`logic-nor` icon. + LOGIC_NOT (IconName): The :code:`logic-not` icon. + LOGIC_OR (IconName): The :code:`logic-or` icon. + LOGIC_XNOR (IconName): The :code:`logic-xnor` icon. + LOGIC_XOR (IconName): The :code:`logic-xor` icon. + LOGIN (IconName): The :code:`login` icon. + LOGOUT (IconName): The :code:`logout` icon. + LOGOUT_2 (IconName): The :code:`logout-2` icon. + LOLLIPOP (IconName): The :code:`lollipop` icon. + LOLLIPOP_OFF (IconName): The :code:`lollipop-off` icon. + LUGGAGE (IconName): The :code:`luggage` icon. + LUGGAGE_OFF (IconName): The :code:`luggage-off` icon. + LUNGS (IconName): The :code:`lungs` icon. + LUNGS_OFF (IconName): The :code:`lungs-off` icon. + MACRO (IconName): The :code:`macro` icon. + MACRO_OFF (IconName): The :code:`macro-off` icon. + MAGNET (IconName): The :code:`magnet` icon. + MAGNET_OFF (IconName): The :code:`magnet-off` icon. + MAIL (IconName): The :code:`mail` icon. + MAIL_AI (IconName): The :code:`mail-ai` icon. + MAIL_BOLT (IconName): The :code:`mail-bolt` icon. + MAIL_CANCEL (IconName): The :code:`mail-cancel` icon. + MAIL_CHECK (IconName): The :code:`mail-check` icon. + MAIL_CODE (IconName): The :code:`mail-code` icon. + MAIL_COG (IconName): The :code:`mail-cog` icon. + MAIL_DOLLAR (IconName): The :code:`mail-dollar` icon. + MAIL_DOWN (IconName): The :code:`mail-down` icon. + MAIL_EXCLAMATION (IconName): The :code:`mail-exclamation` icon. + MAIL_FAST (IconName): The :code:`mail-fast` icon. + MAIL_FILLED (IconName): The :code:`mail-filled` icon. + MAIL_FORWARD (IconName): The :code:`mail-forward` icon. + MAIL_HEART (IconName): The :code:`mail-heart` icon. + MAIL_MINUS (IconName): The :code:`mail-minus` icon. + MAIL_OFF (IconName): The :code:`mail-off` icon. + MAIL_OPENED (IconName): The :code:`mail-opened` icon. + MAIL_OPENED_FILLED (IconName): The :code:`mail-opened-filled` icon. + MAIL_PAUSE (IconName): The :code:`mail-pause` icon. + MAIL_PIN (IconName): The :code:`mail-pin` icon. + MAIL_PLUS (IconName): The :code:`mail-plus` icon. + MAIL_QUESTION (IconName): The :code:`mail-question` icon. + MAIL_SEARCH (IconName): The :code:`mail-search` icon. + MAIL_SHARE (IconName): The :code:`mail-share` icon. + MAIL_STAR (IconName): The :code:`mail-star` icon. + MAIL_UP (IconName): The :code:`mail-up` icon. + MAIL_X (IconName): The :code:`mail-x` icon. + MAILBOX (IconName): The :code:`mailbox` icon. + MAILBOX_OFF (IconName): The :code:`mailbox-off` icon. + MAN (IconName): The :code:`man` icon. + MANUAL_GEARBOX (IconName): The :code:`manual-gearbox` icon. + MAP (IconName): The :code:`map` icon. + MAP_2 (IconName): The :code:`map-2` icon. + MAP_OFF (IconName): The :code:`map-off` icon. + MAP_PIN (IconName): The :code:`map-pin` icon. + MAP_PIN_BOLT (IconName): The :code:`map-pin-bolt` icon. + MAP_PIN_CANCEL (IconName): The :code:`map-pin-cancel` icon. + MAP_PIN_CHECK (IconName): The :code:`map-pin-check` icon. + MAP_PIN_CODE (IconName): The :code:`map-pin-code` icon. + MAP_PIN_COG (IconName): The :code:`map-pin-cog` icon. + MAP_PIN_DOLLAR (IconName): The :code:`map-pin-dollar` icon. + MAP_PIN_DOWN (IconName): The :code:`map-pin-down` icon. + MAP_PIN_EXCLAMATION (IconName): The :code:`map-pin-exclamation` icon. + MAP_PIN_FILLED (IconName): The :code:`map-pin-filled` icon. + MAP_PIN_HEART (IconName): The :code:`map-pin-heart` icon. + MAP_PIN_MINUS (IconName): The :code:`map-pin-minus` icon. + MAP_PIN_OFF (IconName): The :code:`map-pin-off` icon. + MAP_PIN_PAUSE (IconName): The :code:`map-pin-pause` icon. + MAP_PIN_PIN (IconName): The :code:`map-pin-pin` icon. + MAP_PIN_PLUS (IconName): The :code:`map-pin-plus` icon. + MAP_PIN_QUESTION (IconName): The :code:`map-pin-question` icon. + MAP_PIN_SEARCH (IconName): The :code:`map-pin-search` icon. + MAP_PIN_SHARE (IconName): The :code:`map-pin-share` icon. + MAP_PIN_STAR (IconName): The :code:`map-pin-star` icon. + MAP_PIN_UP (IconName): The :code:`map-pin-up` icon. + MAP_PIN_X (IconName): The :code:`map-pin-x` icon. + MAP_PINS (IconName): The :code:`map-pins` icon. + MAP_SEARCH (IconName): The :code:`map-search` icon. + MARKDOWN (IconName): The :code:`markdown` icon. + MARKDOWN_OFF (IconName): The :code:`markdown-off` icon. + MARQUEE (IconName): The :code:`marquee` icon. + MARQUEE_2 (IconName): The :code:`marquee-2` icon. + MARQUEE_OFF (IconName): The :code:`marquee-off` icon. + MARS (IconName): The :code:`mars` icon. + MASK (IconName): The :code:`mask` icon. + MASK_OFF (IconName): The :code:`mask-off` icon. + MASKS_THEATER (IconName): The :code:`masks-theater` icon. + MASKS_THEATER_OFF (IconName): The :code:`masks-theater-off` icon. + MASSAGE (IconName): The :code:`massage` icon. + MATCHSTICK (IconName): The :code:`matchstick` icon. + MATH (IconName): The :code:`math` icon. + MATH_1_DIVIDE_2 (IconName): The :code:`math-1-divide-2` icon. + MATH_1_DIVIDE_3 (IconName): The :code:`math-1-divide-3` icon. + MATH_AVG (IconName): The :code:`math-avg` icon. + MATH_EQUAL_GREATER (IconName): The :code:`math-equal-greater` icon. + MATH_EQUAL_LOWER (IconName): The :code:`math-equal-lower` icon. + MATH_FUNCTION (IconName): The :code:`math-function` icon. + MATH_FUNCTION_OFF (IconName): The :code:`math-function-off` icon. + MATH_FUNCTION_Y (IconName): The :code:`math-function-y` icon. + MATH_GREATER (IconName): The :code:`math-greater` icon. + MATH_INTEGRAL (IconName): The :code:`math-integral` icon. + MATH_INTEGRAL_X (IconName): The :code:`math-integral-x` icon. + MATH_INTEGRALS (IconName): The :code:`math-integrals` icon. + MATH_LOWER (IconName): The :code:`math-lower` icon. + MATH_MAX (IconName): The :code:`math-max` icon. + MATH_MIN (IconName): The :code:`math-min` icon. + MATH_NOT (IconName): The :code:`math-not` icon. + MATH_OFF (IconName): The :code:`math-off` icon. + MATH_PI (IconName): The :code:`math-pi` icon. + MATH_PI_DIVIDE_2 (IconName): The :code:`math-pi-divide-2` icon. + MATH_SYMBOLS (IconName): The :code:`math-symbols` icon. + MATH_X_DIVIDE_2 (IconName): The :code:`math-x-divide-2` icon. + MATH_X_DIVIDE_Y (IconName): The :code:`math-x-divide-y` icon. + MATH_X_DIVIDE_Y_2 (IconName): The :code:`math-x-divide-y-2` icon. + MATH_X_MINUS_X (IconName): The :code:`math-x-minus-x` icon. + MATH_X_MINUS_Y (IconName): The :code:`math-x-minus-y` icon. + MATH_X_PLUS_X (IconName): The :code:`math-x-plus-x` icon. + MATH_X_PLUS_Y (IconName): The :code:`math-x-plus-y` icon. + MATH_XY (IconName): The :code:`math-xy` icon. + MATH_Y_MINUS_Y (IconName): The :code:`math-y-minus-y` icon. + MATH_Y_PLUS_Y (IconName): The :code:`math-y-plus-y` icon. + MAXIMIZE (IconName): The :code:`maximize` icon. + MAXIMIZE_OFF (IconName): The :code:`maximize-off` icon. + MEAT (IconName): The :code:`meat` icon. + MEAT_OFF (IconName): The :code:`meat-off` icon. + MEDAL (IconName): The :code:`medal` icon. + MEDAL_2 (IconName): The :code:`medal-2` icon. + MEDICAL_CROSS (IconName): The :code:`medical-cross` icon. + MEDICAL_CROSS_CIRCLE (IconName): The :code:`medical-cross-circle` icon. + MEDICAL_CROSS_FILLED (IconName): The :code:`medical-cross-filled` icon. + MEDICAL_CROSS_OFF (IconName): The :code:`medical-cross-off` icon. + MEDICINE_SYRUP (IconName): The :code:`medicine-syrup` icon. + MEEPLE (IconName): The :code:`meeple` icon. + MENORAH (IconName): The :code:`menorah` icon. + MENU (IconName): The :code:`menu` icon. + MENU_2 (IconName): The :code:`menu-2` icon. + MENU_DEEP (IconName): The :code:`menu-deep` icon. + MENU_ORDER (IconName): The :code:`menu-order` icon. + MESSAGE (IconName): The :code:`message` icon. + MESSAGE_2 (IconName): The :code:`message-2` icon. + MESSAGE_2_BOLT (IconName): The :code:`message-2-bolt` icon. + MESSAGE_2_CANCEL (IconName): The :code:`message-2-cancel` icon. + MESSAGE_2_CHECK (IconName): The :code:`message-2-check` icon. + MESSAGE_2_CODE (IconName): The :code:`message-2-code` icon. + MESSAGE_2_COG (IconName): The :code:`message-2-cog` icon. + MESSAGE_2_DOLLAR (IconName): The :code:`message-2-dollar` icon. + MESSAGE_2_DOWN (IconName): The :code:`message-2-down` icon. + MESSAGE_2_EXCLAMATION (IconName): The :code:`message-2-exclamation` icon. + MESSAGE_2_HEART (IconName): The :code:`message-2-heart` icon. + MESSAGE_2_MINUS (IconName): The :code:`message-2-minus` icon. + MESSAGE_2_OFF (IconName): The :code:`message-2-off` icon. + MESSAGE_2_PAUSE (IconName): The :code:`message-2-pause` icon. + MESSAGE_2_PIN (IconName): The :code:`message-2-pin` icon. + MESSAGE_2_PLUS (IconName): The :code:`message-2-plus` icon. + MESSAGE_2_QUESTION (IconName): The :code:`message-2-question` icon. + MESSAGE_2_SEARCH (IconName): The :code:`message-2-search` icon. + MESSAGE_2_SHARE (IconName): The :code:`message-2-share` icon. + MESSAGE_2_STAR (IconName): The :code:`message-2-star` icon. + MESSAGE_2_UP (IconName): The :code:`message-2-up` icon. + MESSAGE_2_X (IconName): The :code:`message-2-x` icon. + MESSAGE_BOLT (IconName): The :code:`message-bolt` icon. + MESSAGE_CANCEL (IconName): The :code:`message-cancel` icon. + MESSAGE_CHATBOT (IconName): The :code:`message-chatbot` icon. + MESSAGE_CHECK (IconName): The :code:`message-check` icon. + MESSAGE_CIRCLE (IconName): The :code:`message-circle` icon. + MESSAGE_CIRCLE_2 (IconName): The :code:`message-circle-2` icon. + MESSAGE_CIRCLE_2_FILLED (IconName): The :code:`message-circle-2-filled` icon. + MESSAGE_CIRCLE_BOLT (IconName): The :code:`message-circle-bolt` icon. + MESSAGE_CIRCLE_CANCEL (IconName): The :code:`message-circle-cancel` icon. + MESSAGE_CIRCLE_CHECK (IconName): The :code:`message-circle-check` icon. + MESSAGE_CIRCLE_CODE (IconName): The :code:`message-circle-code` icon. + MESSAGE_CIRCLE_COG (IconName): The :code:`message-circle-cog` icon. + MESSAGE_CIRCLE_DOLLAR (IconName): The :code:`message-circle-dollar` icon. + MESSAGE_CIRCLE_DOWN (IconName): The :code:`message-circle-down` icon. + MESSAGE_CIRCLE_EXCLAMATION (IconName): The :code:`message-circle-exclamation` icon. + MESSAGE_CIRCLE_HEART (IconName): The :code:`message-circle-heart` icon. + MESSAGE_CIRCLE_MINUS (IconName): The :code:`message-circle-minus` icon. + MESSAGE_CIRCLE_OFF (IconName): The :code:`message-circle-off` icon. + MESSAGE_CIRCLE_PAUSE (IconName): The :code:`message-circle-pause` icon. + MESSAGE_CIRCLE_PIN (IconName): The :code:`message-circle-pin` icon. + MESSAGE_CIRCLE_PLUS (IconName): The :code:`message-circle-plus` icon. + MESSAGE_CIRCLE_QUESTION (IconName): The :code:`message-circle-question` icon. + MESSAGE_CIRCLE_SEARCH (IconName): The :code:`message-circle-search` icon. + MESSAGE_CIRCLE_SHARE (IconName): The :code:`message-circle-share` icon. + MESSAGE_CIRCLE_STAR (IconName): The :code:`message-circle-star` icon. + MESSAGE_CIRCLE_UP (IconName): The :code:`message-circle-up` icon. + MESSAGE_CIRCLE_X (IconName): The :code:`message-circle-x` icon. + MESSAGE_CODE (IconName): The :code:`message-code` icon. + MESSAGE_COG (IconName): The :code:`message-cog` icon. + MESSAGE_DOLLAR (IconName): The :code:`message-dollar` icon. + MESSAGE_DOTS (IconName): The :code:`message-dots` icon. + MESSAGE_DOWN (IconName): The :code:`message-down` icon. + MESSAGE_EXCLAMATION (IconName): The :code:`message-exclamation` icon. + MESSAGE_FORWARD (IconName): The :code:`message-forward` icon. + MESSAGE_HEART (IconName): The :code:`message-heart` icon. + MESSAGE_LANGUAGE (IconName): The :code:`message-language` icon. + MESSAGE_MINUS (IconName): The :code:`message-minus` icon. + MESSAGE_OFF (IconName): The :code:`message-off` icon. + MESSAGE_PAUSE (IconName): The :code:`message-pause` icon. + MESSAGE_PIN (IconName): The :code:`message-pin` icon. + MESSAGE_PLUS (IconName): The :code:`message-plus` icon. + MESSAGE_QUESTION (IconName): The :code:`message-question` icon. + MESSAGE_REPORT (IconName): The :code:`message-report` icon. + MESSAGE_SEARCH (IconName): The :code:`message-search` icon. + MESSAGE_SHARE (IconName): The :code:`message-share` icon. + MESSAGE_STAR (IconName): The :code:`message-star` icon. + MESSAGE_UP (IconName): The :code:`message-up` icon. + MESSAGE_X (IconName): The :code:`message-x` icon. + MESSAGES (IconName): The :code:`messages` icon. + MESSAGES_OFF (IconName): The :code:`messages-off` icon. + METEOR (IconName): The :code:`meteor` icon. + METEOR_OFF (IconName): The :code:`meteor-off` icon. + MICHELIN_BIB_GOURMAND (IconName): The :code:`michelin-bib-gourmand` icon. + MICHELIN_STAR (IconName): The :code:`michelin-star` icon. + MICHELIN_STAR_GREEN (IconName): The :code:`michelin-star-green` icon. + MICKEY (IconName): The :code:`mickey` icon. + MICKEY_FILLED (IconName): The :code:`mickey-filled` icon. + MICROPHONE (IconName): The :code:`microphone` icon. + MICROPHONE_2 (IconName): The :code:`microphone-2` icon. + MICROPHONE_2_OFF (IconName): The :code:`microphone-2-off` icon. + MICROPHONE_OFF (IconName): The :code:`microphone-off` icon. + MICROSCOPE (IconName): The :code:`microscope` icon. + MICROSCOPE_OFF (IconName): The :code:`microscope-off` icon. + MICROWAVE (IconName): The :code:`microwave` icon. + MICROWAVE_OFF (IconName): The :code:`microwave-off` icon. + MILITARY_AWARD (IconName): The :code:`military-award` icon. + MILITARY_RANK (IconName): The :code:`military-rank` icon. + MILK (IconName): The :code:`milk` icon. + MILK_OFF (IconName): The :code:`milk-off` icon. + MILKSHAKE (IconName): The :code:`milkshake` icon. + MINIMIZE (IconName): The :code:`minimize` icon. + MINUS (IconName): The :code:`minus` icon. + MINUS_VERTICAL (IconName): The :code:`minus-vertical` icon. + MIST (IconName): The :code:`mist` icon. + MIST_OFF (IconName): The :code:`mist-off` icon. + MOBILEDATA (IconName): The :code:`mobiledata` icon. + MOBILEDATA_OFF (IconName): The :code:`mobiledata-off` icon. + MONEYBAG (IconName): The :code:`moneybag` icon. + MOOD_ANGRY (IconName): The :code:`mood-angry` icon. + MOOD_ANNOYED (IconName): The :code:`mood-annoyed` icon. + MOOD_ANNOYED_2 (IconName): The :code:`mood-annoyed-2` icon. + MOOD_BOY (IconName): The :code:`mood-boy` icon. + MOOD_CHECK (IconName): The :code:`mood-check` icon. + MOOD_COG (IconName): The :code:`mood-cog` icon. + MOOD_CONFUZED (IconName): The :code:`mood-confuzed` icon. + MOOD_CONFUZED_FILLED (IconName): The :code:`mood-confuzed-filled` icon. + MOOD_CRAZY_HAPPY (IconName): The :code:`mood-crazy-happy` icon. + MOOD_CRY (IconName): The :code:`mood-cry` icon. + MOOD_DOLLAR (IconName): The :code:`mood-dollar` icon. + MOOD_EDIT (IconName): The :code:`mood-edit` icon. + MOOD_EMPTY (IconName): The :code:`mood-empty` icon. + MOOD_EMPTY_FILLED (IconName): The :code:`mood-empty-filled` icon. + MOOD_HAPPY (IconName): The :code:`mood-happy` icon. + MOOD_HAPPY_FILLED (IconName): The :code:`mood-happy-filled` icon. + MOOD_HEART (IconName): The :code:`mood-heart` icon. + MOOD_KID (IconName): The :code:`mood-kid` icon. + MOOD_KID_FILLED (IconName): The :code:`mood-kid-filled` icon. + MOOD_LOOK_LEFT (IconName): The :code:`mood-look-left` icon. + MOOD_LOOK_RIGHT (IconName): The :code:`mood-look-right` icon. + MOOD_MINUS (IconName): The :code:`mood-minus` icon. + MOOD_NERD (IconName): The :code:`mood-nerd` icon. + MOOD_NERVOUS (IconName): The :code:`mood-nervous` icon. + MOOD_NEUTRAL (IconName): The :code:`mood-neutral` icon. + MOOD_NEUTRAL_FILLED (IconName): The :code:`mood-neutral-filled` icon. + MOOD_OFF (IconName): The :code:`mood-off` icon. + MOOD_PIN (IconName): The :code:`mood-pin` icon. + MOOD_PLUS (IconName): The :code:`mood-plus` icon. + MOOD_SAD (IconName): The :code:`mood-sad` icon. + MOOD_SAD_2 (IconName): The :code:`mood-sad-2` icon. + MOOD_SAD_DIZZY (IconName): The :code:`mood-sad-dizzy` icon. + MOOD_SAD_FILLED (IconName): The :code:`mood-sad-filled` icon. + MOOD_SAD_SQUINT (IconName): The :code:`mood-sad-squint` icon. + MOOD_SEARCH (IconName): The :code:`mood-search` icon. + MOOD_SHARE (IconName): The :code:`mood-share` icon. + MOOD_SICK (IconName): The :code:`mood-sick` icon. + MOOD_SILENCE (IconName): The :code:`mood-silence` icon. + MOOD_SING (IconName): The :code:`mood-sing` icon. + MOOD_SMILE (IconName): The :code:`mood-smile` icon. + MOOD_SMILE_BEAM (IconName): The :code:`mood-smile-beam` icon. + MOOD_SMILE_DIZZY (IconName): The :code:`mood-smile-dizzy` icon. + MOOD_SMILE_FILLED (IconName): The :code:`mood-smile-filled` icon. + MOOD_SUPRISED (IconName): The :code:`mood-suprised` icon. + MOOD_TONGUE (IconName): The :code:`mood-tongue` icon. + MOOD_TONGUE_WINK (IconName): The :code:`mood-tongue-wink` icon. + MOOD_TONGUE_WINK_2 (IconName): The :code:`mood-tongue-wink-2` icon. + MOOD_UNAMUSED (IconName): The :code:`mood-unamused` icon. + MOOD_UP (IconName): The :code:`mood-up` icon. + MOOD_WINK (IconName): The :code:`mood-wink` icon. + MOOD_WINK_2 (IconName): The :code:`mood-wink-2` icon. + MOOD_WRRR (IconName): The :code:`mood-wrrr` icon. + MOOD_X (IconName): The :code:`mood-x` icon. + MOOD_XD (IconName): The :code:`mood-xd` icon. + MOON (IconName): The :code:`moon` icon. + MOON_2 (IconName): The :code:`moon-2` icon. + MOON_FILLED (IconName): The :code:`moon-filled` icon. + MOON_OFF (IconName): The :code:`moon-off` icon. + MOON_STARS (IconName): The :code:`moon-stars` icon. + MOPED (IconName): The :code:`moped` icon. + MOTORBIKE (IconName): The :code:`motorbike` icon. + MOUNTAIN (IconName): The :code:`mountain` icon. + MOUNTAIN_OFF (IconName): The :code:`mountain-off` icon. + MOUSE (IconName): The :code:`mouse` icon. + MOUSE_2 (IconName): The :code:`mouse-2` icon. + MOUSE_OFF (IconName): The :code:`mouse-off` icon. + MOUSTACHE (IconName): The :code:`moustache` icon. + MOVIE (IconName): The :code:`movie` icon. + MOVIE_OFF (IconName): The :code:`movie-off` icon. + MUG (IconName): The :code:`mug` icon. + MUG_OFF (IconName): The :code:`mug-off` icon. + MULTIPLIER_0_5X (IconName): The :code:`multiplier-0-5x` icon. + MULTIPLIER_1_5X (IconName): The :code:`multiplier-1-5x` icon. + MULTIPLIER_1X (IconName): The :code:`multiplier-1x` icon. + MULTIPLIER_2X (IconName): The :code:`multiplier-2x` icon. + MUSHROOM (IconName): The :code:`mushroom` icon. + MUSHROOM_FILLED (IconName): The :code:`mushroom-filled` icon. + MUSHROOM_OFF (IconName): The :code:`mushroom-off` icon. + MUSIC (IconName): The :code:`music` icon. + MUSIC_OFF (IconName): The :code:`music-off` icon. + NAVIGATION (IconName): The :code:`navigation` icon. + NAVIGATION_FILLED (IconName): The :code:`navigation-filled` icon. + NAVIGATION_NORTH (IconName): The :code:`navigation-north` icon. + NAVIGATION_OFF (IconName): The :code:`navigation-off` icon. + NEEDLE (IconName): The :code:`needle` icon. + NEEDLE_THREAD (IconName): The :code:`needle-thread` icon. + NETWORK (IconName): The :code:`network` icon. + NETWORK_OFF (IconName): The :code:`network-off` icon. + NEW_SECTION (IconName): The :code:`new-section` icon. + NEWS (IconName): The :code:`news` icon. + NEWS_OFF (IconName): The :code:`news-off` icon. + NFC (IconName): The :code:`nfc` icon. + NFC_OFF (IconName): The :code:`nfc-off` icon. + NO_COPYRIGHT (IconName): The :code:`no-copyright` icon. + NO_CREATIVE_COMMONS (IconName): The :code:`no-creative-commons` icon. + NO_DERIVATIVES (IconName): The :code:`no-derivatives` icon. + NORTH_STAR (IconName): The :code:`north-star` icon. + NOTE (IconName): The :code:`note` icon. + NOTE_OFF (IconName): The :code:`note-off` icon. + NOTEBOOK (IconName): The :code:`notebook` icon. + NOTEBOOK_OFF (IconName): The :code:`notebook-off` icon. + NOTES (IconName): The :code:`notes` icon. + NOTES_OFF (IconName): The :code:`notes-off` icon. + NOTIFICATION (IconName): The :code:`notification` icon. + NOTIFICATION_OFF (IconName): The :code:`notification-off` icon. + NUMBER (IconName): The :code:`number` icon. + NUMBER_0 (IconName): The :code:`number-0` icon. + NUMBER_1 (IconName): The :code:`number-1` icon. + NUMBER_2 (IconName): The :code:`number-2` icon. + NUMBER_3 (IconName): The :code:`number-3` icon. + NUMBER_4 (IconName): The :code:`number-4` icon. + NUMBER_5 (IconName): The :code:`number-5` icon. + NUMBER_6 (IconName): The :code:`number-6` icon. + NUMBER_7 (IconName): The :code:`number-7` icon. + NUMBER_8 (IconName): The :code:`number-8` icon. + NUMBER_9 (IconName): The :code:`number-9` icon. + NUMBERS (IconName): The :code:`numbers` icon. + NURSE (IconName): The :code:`nurse` icon. + OCTAGON (IconName): The :code:`octagon` icon. + OCTAGON_FILLED (IconName): The :code:`octagon-filled` icon. + OCTAGON_OFF (IconName): The :code:`octagon-off` icon. + OCTAHEDRON (IconName): The :code:`octahedron` icon. + OCTAHEDRON_OFF (IconName): The :code:`octahedron-off` icon. + OCTAHEDRON_PLUS (IconName): The :code:`octahedron-plus` icon. + OLD (IconName): The :code:`old` icon. + OLYMPICS (IconName): The :code:`olympics` icon. + OLYMPICS_OFF (IconName): The :code:`olympics-off` icon. + OM (IconName): The :code:`om` icon. + OMEGA (IconName): The :code:`omega` icon. + OUTBOUND (IconName): The :code:`outbound` icon. + OUTLET (IconName): The :code:`outlet` icon. + OVAL (IconName): The :code:`oval` icon. + OVAL_FILLED (IconName): The :code:`oval-filled` icon. + OVAL_VERTICAL (IconName): The :code:`oval-vertical` icon. + OVAL_VERTICAL_FILLED (IconName): The :code:`oval-vertical-filled` icon. + OVERLINE (IconName): The :code:`overline` icon. + PACKAGE (IconName): The :code:`package` icon. + PACKAGE_EXPORT (IconName): The :code:`package-export` icon. + PACKAGE_IMPORT (IconName): The :code:`package-import` icon. + PACKAGE_OFF (IconName): The :code:`package-off` icon. + PACKAGES (IconName): The :code:`packages` icon. + PACMAN (IconName): The :code:`pacman` icon. + PAGE_BREAK (IconName): The :code:`page-break` icon. + PAINT (IconName): The :code:`paint` icon. + PAINT_FILLED (IconName): The :code:`paint-filled` icon. + PAINT_OFF (IconName): The :code:`paint-off` icon. + PALETTE (IconName): The :code:`palette` icon. + PALETTE_OFF (IconName): The :code:`palette-off` icon. + PANORAMA_HORIZONTAL (IconName): The :code:`panorama-horizontal` icon. + PANORAMA_HORIZONTAL_OFF (IconName): The :code:`panorama-horizontal-off` icon. + PANORAMA_VERTICAL (IconName): The :code:`panorama-vertical` icon. + PANORAMA_VERTICAL_OFF (IconName): The :code:`panorama-vertical-off` icon. + PAPER_BAG (IconName): The :code:`paper-bag` icon. + PAPER_BAG_OFF (IconName): The :code:`paper-bag-off` icon. + PAPERCLIP (IconName): The :code:`paperclip` icon. + PARACHUTE (IconName): The :code:`parachute` icon. + PARACHUTE_OFF (IconName): The :code:`parachute-off` icon. + PARENTHESES (IconName): The :code:`parentheses` icon. + PARENTHESES_OFF (IconName): The :code:`parentheses-off` icon. + PARKING (IconName): The :code:`parking` icon. + PARKING_OFF (IconName): The :code:`parking-off` icon. + PASSWORD (IconName): The :code:`password` icon. + PAW (IconName): The :code:`paw` icon. + PAW_FILLED (IconName): The :code:`paw-filled` icon. + PAW_OFF (IconName): The :code:`paw-off` icon. + PDF (IconName): The :code:`pdf` icon. + PEACE (IconName): The :code:`peace` icon. + PENCIL (IconName): The :code:`pencil` icon. + PENCIL_MINUS (IconName): The :code:`pencil-minus` icon. + PENCIL_OFF (IconName): The :code:`pencil-off` icon. + PENCIL_PLUS (IconName): The :code:`pencil-plus` icon. + PENNANT (IconName): The :code:`pennant` icon. + PENNANT_2 (IconName): The :code:`pennant-2` icon. + PENNANT_2_FILLED (IconName): The :code:`pennant-2-filled` icon. + PENNANT_FILLED (IconName): The :code:`pennant-filled` icon. + PENNANT_OFF (IconName): The :code:`pennant-off` icon. + PENTAGON (IconName): The :code:`pentagon` icon. + PENTAGON_FILLED (IconName): The :code:`pentagon-filled` icon. + PENTAGON_OFF (IconName): The :code:`pentagon-off` icon. + PENTAGRAM (IconName): The :code:`pentagram` icon. + PEPPER (IconName): The :code:`pepper` icon. + PEPPER_OFF (IconName): The :code:`pepper-off` icon. + PERCENTAGE (IconName): The :code:`percentage` icon. + PERFUME (IconName): The :code:`perfume` icon. + PERSPECTIVE (IconName): The :code:`perspective` icon. + PERSPECTIVE_OFF (IconName): The :code:`perspective-off` icon. + PHONE (IconName): The :code:`phone` icon. + PHONE_CALL (IconName): The :code:`phone-call` icon. + PHONE_CALLING (IconName): The :code:`phone-calling` icon. + PHONE_CHECK (IconName): The :code:`phone-check` icon. + PHONE_FILLED (IconName): The :code:`phone-filled` icon. + PHONE_INCOMING (IconName): The :code:`phone-incoming` icon. + PHONE_OFF (IconName): The :code:`phone-off` icon. + PHONE_OUTGOING (IconName): The :code:`phone-outgoing` icon. + PHONE_PAUSE (IconName): The :code:`phone-pause` icon. + PHONE_PLUS (IconName): The :code:`phone-plus` icon. + PHONE_X (IconName): The :code:`phone-x` icon. + PHOTO (IconName): The :code:`photo` icon. + PHOTO_AI (IconName): The :code:`photo-ai` icon. + PHOTO_BOLT (IconName): The :code:`photo-bolt` icon. + PHOTO_CANCEL (IconName): The :code:`photo-cancel` icon. + PHOTO_CHECK (IconName): The :code:`photo-check` icon. + PHOTO_CODE (IconName): The :code:`photo-code` icon. + PHOTO_COG (IconName): The :code:`photo-cog` icon. + PHOTO_DOLLAR (IconName): The :code:`photo-dollar` icon. + PHOTO_DOWN (IconName): The :code:`photo-down` icon. + PHOTO_EDIT (IconName): The :code:`photo-edit` icon. + PHOTO_EXCLAMATION (IconName): The :code:`photo-exclamation` icon. + PHOTO_FILLED (IconName): The :code:`photo-filled` icon. + PHOTO_HEART (IconName): The :code:`photo-heart` icon. + PHOTO_MINUS (IconName): The :code:`photo-minus` icon. + PHOTO_OFF (IconName): The :code:`photo-off` icon. + PHOTO_PAUSE (IconName): The :code:`photo-pause` icon. + PHOTO_PIN (IconName): The :code:`photo-pin` icon. + PHOTO_PLUS (IconName): The :code:`photo-plus` icon. + PHOTO_QUESTION (IconName): The :code:`photo-question` icon. + PHOTO_SEARCH (IconName): The :code:`photo-search` icon. + PHOTO_SENSOR (IconName): The :code:`photo-sensor` icon. + PHOTO_SENSOR_2 (IconName): The :code:`photo-sensor-2` icon. + PHOTO_SENSOR_3 (IconName): The :code:`photo-sensor-3` icon. + PHOTO_SHARE (IconName): The :code:`photo-share` icon. + PHOTO_SHIELD (IconName): The :code:`photo-shield` icon. + PHOTO_STAR (IconName): The :code:`photo-star` icon. + PHOTO_UP (IconName): The :code:`photo-up` icon. + PHOTO_X (IconName): The :code:`photo-x` icon. + PHYSOTHERAPIST (IconName): The :code:`physotherapist` icon. + PIANO (IconName): The :code:`piano` icon. + PICK (IconName): The :code:`pick` icon. + PICTURE_IN_PICTURE (IconName): The :code:`picture-in-picture` icon. + PICTURE_IN_PICTURE_OFF (IconName): The :code:`picture-in-picture-off` icon. + PICTURE_IN_PICTURE_ON (IconName): The :code:`picture-in-picture-on` icon. + PICTURE_IN_PICTURE_TOP (IconName): The :code:`picture-in-picture-top` icon. + PIG (IconName): The :code:`pig` icon. + PIG_MONEY (IconName): The :code:`pig-money` icon. + PIG_OFF (IconName): The :code:`pig-off` icon. + PILCROW (IconName): The :code:`pilcrow` icon. + PILL (IconName): The :code:`pill` icon. + PILL_OFF (IconName): The :code:`pill-off` icon. + PILLS (IconName): The :code:`pills` icon. + PIN (IconName): The :code:`pin` icon. + PIN_FILLED (IconName): The :code:`pin-filled` icon. + PING_PONG (IconName): The :code:`ping-pong` icon. + PINNED (IconName): The :code:`pinned` icon. + PINNED_FILLED (IconName): The :code:`pinned-filled` icon. + PINNED_OFF (IconName): The :code:`pinned-off` icon. + PIZZA (IconName): The :code:`pizza` icon. + PIZZA_OFF (IconName): The :code:`pizza-off` icon. + PLACEHOLDER (IconName): The :code:`placeholder` icon. + PLANE (IconName): The :code:`plane` icon. + PLANE_ARRIVAL (IconName): The :code:`plane-arrival` icon. + PLANE_DEPARTURE (IconName): The :code:`plane-departure` icon. + PLANE_INFLIGHT (IconName): The :code:`plane-inflight` icon. + PLANE_OFF (IconName): The :code:`plane-off` icon. + PLANE_TILT (IconName): The :code:`plane-tilt` icon. + PLANET (IconName): The :code:`planet` icon. + PLANET_OFF (IconName): The :code:`planet-off` icon. + PLANT (IconName): The :code:`plant` icon. + PLANT_2 (IconName): The :code:`plant-2` icon. + PLANT_2_OFF (IconName): The :code:`plant-2-off` icon. + PLANT_OFF (IconName): The :code:`plant-off` icon. + PLAY_BASKETBALL (IconName): The :code:`play-basketball` icon. + PLAY_CARD (IconName): The :code:`play-card` icon. + PLAY_CARD_OFF (IconName): The :code:`play-card-off` icon. + PLAY_FOOTBALL (IconName): The :code:`play-football` icon. + PLAY_HANDBALL (IconName): The :code:`play-handball` icon. + PLAY_VOLLEYBALL (IconName): The :code:`play-volleyball` icon. + PLAYER_EJECT (IconName): The :code:`player-eject` icon. + PLAYER_EJECT_FILLED (IconName): The :code:`player-eject-filled` icon. + PLAYER_PAUSE (IconName): The :code:`player-pause` icon. + PLAYER_PAUSE_FILLED (IconName): The :code:`player-pause-filled` icon. + PLAYER_PLAY (IconName): The :code:`player-play` icon. + PLAYER_PLAY_FILLED (IconName): The :code:`player-play-filled` icon. + PLAYER_RECORD (IconName): The :code:`player-record` icon. + PLAYER_RECORD_FILLED (IconName): The :code:`player-record-filled` icon. + PLAYER_SKIP_BACK (IconName): The :code:`player-skip-back` icon. + PLAYER_SKIP_BACK_FILLED (IconName): The :code:`player-skip-back-filled` icon. + PLAYER_SKIP_FORWARD (IconName): The :code:`player-skip-forward` icon. + PLAYER_SKIP_FORWARD_FILLED (IconName): The :code:`player-skip-forward-filled` icon. + PLAYER_STOP (IconName): The :code:`player-stop` icon. + PLAYER_STOP_FILLED (IconName): The :code:`player-stop-filled` icon. + PLAYER_TRACK_NEXT (IconName): The :code:`player-track-next` icon. + PLAYER_TRACK_NEXT_FILLED (IconName): The :code:`player-track-next-filled` icon. + PLAYER_TRACK_PREV (IconName): The :code:`player-track-prev` icon. + PLAYER_TRACK_PREV_FILLED (IconName): The :code:`player-track-prev-filled` icon. + PLAYLIST (IconName): The :code:`playlist` icon. + PLAYLIST_ADD (IconName): The :code:`playlist-add` icon. + PLAYLIST_OFF (IconName): The :code:`playlist-off` icon. + PLAYLIST_X (IconName): The :code:`playlist-x` icon. + PLAYSTATION_CIRCLE (IconName): The :code:`playstation-circle` icon. + PLAYSTATION_SQUARE (IconName): The :code:`playstation-square` icon. + PLAYSTATION_TRIANGLE (IconName): The :code:`playstation-triangle` icon. + PLAYSTATION_X (IconName): The :code:`playstation-x` icon. + PLUG (IconName): The :code:`plug` icon. + PLUG_CONNECTED (IconName): The :code:`plug-connected` icon. + PLUG_CONNECTED_X (IconName): The :code:`plug-connected-x` icon. + PLUG_OFF (IconName): The :code:`plug-off` icon. + PLUG_X (IconName): The :code:`plug-x` icon. + PLUS (IconName): The :code:`plus` icon. + PLUS_EQUAL (IconName): The :code:`plus-equal` icon. + PLUS_MINUS (IconName): The :code:`plus-minus` icon. + PNG (IconName): The :code:`png` icon. + PODIUM (IconName): The :code:`podium` icon. + PODIUM_OFF (IconName): The :code:`podium-off` icon. + POINT (IconName): The :code:`point` icon. + POINT_FILLED (IconName): The :code:`point-filled` icon. + POINT_OFF (IconName): The :code:`point-off` icon. + POINTER (IconName): The :code:`pointer` icon. + POINTER_BOLT (IconName): The :code:`pointer-bolt` icon. + POINTER_CANCEL (IconName): The :code:`pointer-cancel` icon. + POINTER_CHECK (IconName): The :code:`pointer-check` icon. + POINTER_CODE (IconName): The :code:`pointer-code` icon. + POINTER_COG (IconName): The :code:`pointer-cog` icon. + POINTER_DOLLAR (IconName): The :code:`pointer-dollar` icon. + POINTER_DOWN (IconName): The :code:`pointer-down` icon. + POINTER_EXCLAMATION (IconName): The :code:`pointer-exclamation` icon. + POINTER_HEART (IconName): The :code:`pointer-heart` icon. + POINTER_MINUS (IconName): The :code:`pointer-minus` icon. + POINTER_OFF (IconName): The :code:`pointer-off` icon. + POINTER_PAUSE (IconName): The :code:`pointer-pause` icon. + POINTER_PIN (IconName): The :code:`pointer-pin` icon. + POINTER_PLUS (IconName): The :code:`pointer-plus` icon. + POINTER_QUESTION (IconName): The :code:`pointer-question` icon. + POINTER_SEARCH (IconName): The :code:`pointer-search` icon. + POINTER_SHARE (IconName): The :code:`pointer-share` icon. + POINTER_STAR (IconName): The :code:`pointer-star` icon. + POINTER_UP (IconName): The :code:`pointer-up` icon. + POINTER_X (IconName): The :code:`pointer-x` icon. + POKEBALL (IconName): The :code:`pokeball` icon. + POKEBALL_OFF (IconName): The :code:`pokeball-off` icon. + POKER_CHIP (IconName): The :code:`poker-chip` icon. + POLAROID (IconName): The :code:`polaroid` icon. + POLAROID_FILLED (IconName): The :code:`polaroid-filled` icon. + POLYGON (IconName): The :code:`polygon` icon. + POLYGON_OFF (IconName): The :code:`polygon-off` icon. + POO (IconName): The :code:`poo` icon. + POOL (IconName): The :code:`pool` icon. + POOL_OFF (IconName): The :code:`pool-off` icon. + POWER (IconName): The :code:`power` icon. + PRAY (IconName): The :code:`pray` icon. + PREMIUM_RIGHTS (IconName): The :code:`premium-rights` icon. + PRESCRIPTION (IconName): The :code:`prescription` icon. + PRESENTATION (IconName): The :code:`presentation` icon. + PRESENTATION_ANALYTICS (IconName): The :code:`presentation-analytics` icon. + PRESENTATION_OFF (IconName): The :code:`presentation-off` icon. + PRINTER (IconName): The :code:`printer` icon. + PRINTER_OFF (IconName): The :code:`printer-off` icon. + PRISM (IconName): The :code:`prism` icon. + PRISM_OFF (IconName): The :code:`prism-off` icon. + PRISM_PLUS (IconName): The :code:`prism-plus` icon. + PRISON (IconName): The :code:`prison` icon. + PROGRESS (IconName): The :code:`progress` icon. + PROGRESS_ALERT (IconName): The :code:`progress-alert` icon. + PROGRESS_BOLT (IconName): The :code:`progress-bolt` icon. + PROGRESS_CHECK (IconName): The :code:`progress-check` icon. + PROGRESS_DOWN (IconName): The :code:`progress-down` icon. + PROGRESS_HELP (IconName): The :code:`progress-help` icon. + PROGRESS_X (IconName): The :code:`progress-x` icon. + PROMPT (IconName): The :code:`prompt` icon. + PROPELLER (IconName): The :code:`propeller` icon. + PROPELLER_OFF (IconName): The :code:`propeller-off` icon. + PUMPKIN_SCARY (IconName): The :code:`pumpkin-scary` icon. + PUZZLE (IconName): The :code:`puzzle` icon. + PUZZLE_2 (IconName): The :code:`puzzle-2` icon. + PUZZLE_FILLED (IconName): The :code:`puzzle-filled` icon. + PUZZLE_OFF (IconName): The :code:`puzzle-off` icon. + PYRAMID (IconName): The :code:`pyramid` icon. + PYRAMID_OFF (IconName): The :code:`pyramid-off` icon. + PYRAMID_PLUS (IconName): The :code:`pyramid-plus` icon. + QRCODE (IconName): The :code:`qrcode` icon. + QRCODE_OFF (IconName): The :code:`qrcode-off` icon. + QUESTION_MARK (IconName): The :code:`question-mark` icon. + QUOTE (IconName): The :code:`quote` icon. + QUOTE_OFF (IconName): The :code:`quote-off` icon. + RADAR (IconName): The :code:`radar` icon. + RADAR_2 (IconName): The :code:`radar-2` icon. + RADAR_OFF (IconName): The :code:`radar-off` icon. + RADIO (IconName): The :code:`radio` icon. + RADIO_OFF (IconName): The :code:`radio-off` icon. + RADIOACTIVE (IconName): The :code:`radioactive` icon. + RADIOACTIVE_FILLED (IconName): The :code:`radioactive-filled` icon. + RADIOACTIVE_OFF (IconName): The :code:`radioactive-off` icon. + RADIUS_BOTTOM_LEFT (IconName): The :code:`radius-bottom-left` icon. + RADIUS_BOTTOM_RIGHT (IconName): The :code:`radius-bottom-right` icon. + RADIUS_TOP_LEFT (IconName): The :code:`radius-top-left` icon. + RADIUS_TOP_RIGHT (IconName): The :code:`radius-top-right` icon. + RAINBOW (IconName): The :code:`rainbow` icon. + RAINBOW_OFF (IconName): The :code:`rainbow-off` icon. + RATING_12_PLUS (IconName): The :code:`rating-12-plus` icon. + RATING_14_PLUS (IconName): The :code:`rating-14-plus` icon. + RATING_16_PLUS (IconName): The :code:`rating-16-plus` icon. + RATING_18_PLUS (IconName): The :code:`rating-18-plus` icon. + RATING_21_PLUS (IconName): The :code:`rating-21-plus` icon. + RAZOR (IconName): The :code:`razor` icon. + RAZOR_ELECTRIC (IconName): The :code:`razor-electric` icon. + RECEIPT (IconName): The :code:`receipt` icon. + RECEIPT_2 (IconName): The :code:`receipt-2` icon. + RECEIPT_OFF (IconName): The :code:`receipt-off` icon. + RECEIPT_REFUND (IconName): The :code:`receipt-refund` icon. + RECEIPT_TAX (IconName): The :code:`receipt-tax` icon. + RECHARGING (IconName): The :code:`recharging` icon. + RECORD_MAIL (IconName): The :code:`record-mail` icon. + RECORD_MAIL_OFF (IconName): The :code:`record-mail-off` icon. + RECTANGLE (IconName): The :code:`rectangle` icon. + RECTANGLE_FILLED (IconName): The :code:`rectangle-filled` icon. + RECTANGLE_ROUNDED_BOTTOM (IconName): The :code:`rectangle-rounded-bottom` icon. + RECTANGLE_ROUNDED_TOP (IconName): The :code:`rectangle-rounded-top` icon. + RECTANGLE_VERTICAL (IconName): The :code:`rectangle-vertical` icon. + RECTANGLE_VERTICAL_FILLED (IconName): The :code:`rectangle-vertical-filled` icon. + RECTANGULAR_PRISM (IconName): The :code:`rectangular-prism` icon. + RECTANGULAR_PRISM_OFF (IconName): The :code:`rectangular-prism-off` icon. + RECTANGULAR_PRISM_PLUS (IconName): The :code:`rectangular-prism-plus` icon. + RECYCLE (IconName): The :code:`recycle` icon. + RECYCLE_OFF (IconName): The :code:`recycle-off` icon. + REFRESH (IconName): The :code:`refresh` icon. + REFRESH_ALERT (IconName): The :code:`refresh-alert` icon. + REFRESH_DOT (IconName): The :code:`refresh-dot` icon. + REFRESH_OFF (IconName): The :code:`refresh-off` icon. + REGEX (IconName): The :code:`regex` icon. + REGEX_OFF (IconName): The :code:`regex-off` icon. + REGISTERED (IconName): The :code:`registered` icon. + RELATION_MANY_TO_MANY (IconName): The :code:`relation-many-to-many` icon. + RELATION_ONE_TO_MANY (IconName): The :code:`relation-one-to-many` icon. + RELATION_ONE_TO_ONE (IconName): The :code:`relation-one-to-one` icon. + RELOAD (IconName): The :code:`reload` icon. + REPEAT (IconName): The :code:`repeat` icon. + REPEAT_OFF (IconName): The :code:`repeat-off` icon. + REPEAT_ONCE (IconName): The :code:`repeat-once` icon. + REPLACE (IconName): The :code:`replace` icon. + REPLACE_FILLED (IconName): The :code:`replace-filled` icon. + REPLACE_OFF (IconName): The :code:`replace-off` icon. + REPORT (IconName): The :code:`report` icon. + REPORT_ANALYTICS (IconName): The :code:`report-analytics` icon. + REPORT_MEDICAL (IconName): The :code:`report-medical` icon. + REPORT_MONEY (IconName): The :code:`report-money` icon. + REPORT_OFF (IconName): The :code:`report-off` icon. + REPORT_SEARCH (IconName): The :code:`report-search` icon. + RESERVED_LINE (IconName): The :code:`reserved-line` icon. + RESIZE (IconName): The :code:`resize` icon. + RESTORE (IconName): The :code:`restore` icon. + REWIND_BACKWARD_10 (IconName): The :code:`rewind-backward-10` icon. + REWIND_BACKWARD_15 (IconName): The :code:`rewind-backward-15` icon. + REWIND_BACKWARD_20 (IconName): The :code:`rewind-backward-20` icon. + REWIND_BACKWARD_30 (IconName): The :code:`rewind-backward-30` icon. + REWIND_BACKWARD_40 (IconName): The :code:`rewind-backward-40` icon. + REWIND_BACKWARD_5 (IconName): The :code:`rewind-backward-5` icon. + REWIND_BACKWARD_50 (IconName): The :code:`rewind-backward-50` icon. + REWIND_BACKWARD_60 (IconName): The :code:`rewind-backward-60` icon. + REWIND_FORWARD_10 (IconName): The :code:`rewind-forward-10` icon. + REWIND_FORWARD_15 (IconName): The :code:`rewind-forward-15` icon. + REWIND_FORWARD_20 (IconName): The :code:`rewind-forward-20` icon. + REWIND_FORWARD_30 (IconName): The :code:`rewind-forward-30` icon. + REWIND_FORWARD_40 (IconName): The :code:`rewind-forward-40` icon. + REWIND_FORWARD_5 (IconName): The :code:`rewind-forward-5` icon. + REWIND_FORWARD_50 (IconName): The :code:`rewind-forward-50` icon. + REWIND_FORWARD_60 (IconName): The :code:`rewind-forward-60` icon. + RIBBON_HEALTH (IconName): The :code:`ribbon-health` icon. + RINGS (IconName): The :code:`rings` icon. + RIPPLE (IconName): The :code:`ripple` icon. + RIPPLE_OFF (IconName): The :code:`ripple-off` icon. + ROAD (IconName): The :code:`road` icon. + ROAD_OFF (IconName): The :code:`road-off` icon. + ROAD_SIGN (IconName): The :code:`road-sign` icon. + ROBOT (IconName): The :code:`robot` icon. + ROBOT_OFF (IconName): The :code:`robot-off` icon. + ROCKET (IconName): The :code:`rocket` icon. + ROCKET_OFF (IconName): The :code:`rocket-off` icon. + ROLLER_SKATING (IconName): The :code:`roller-skating` icon. + ROLLERCOASTER (IconName): The :code:`rollercoaster` icon. + ROLLERCOASTER_OFF (IconName): The :code:`rollercoaster-off` icon. + ROSETTE (IconName): The :code:`rosette` icon. + ROSETTE_FILLED (IconName): The :code:`rosette-filled` icon. + ROSETTE_NUMBER_0 (IconName): The :code:`rosette-number-0` icon. + ROSETTE_NUMBER_1 (IconName): The :code:`rosette-number-1` icon. + ROSETTE_NUMBER_2 (IconName): The :code:`rosette-number-2` icon. + ROSETTE_NUMBER_3 (IconName): The :code:`rosette-number-3` icon. + ROSETTE_NUMBER_4 (IconName): The :code:`rosette-number-4` icon. + ROSETTE_NUMBER_5 (IconName): The :code:`rosette-number-5` icon. + ROSETTE_NUMBER_6 (IconName): The :code:`rosette-number-6` icon. + ROSETTE_NUMBER_7 (IconName): The :code:`rosette-number-7` icon. + ROSETTE_NUMBER_8 (IconName): The :code:`rosette-number-8` icon. + ROSETTE_NUMBER_9 (IconName): The :code:`rosette-number-9` icon. + ROTATE (IconName): The :code:`rotate` icon. + ROTATE_2 (IconName): The :code:`rotate-2` icon. + ROTATE_360 (IconName): The :code:`rotate-360` icon. + ROTATE_CLOCKWISE (IconName): The :code:`rotate-clockwise` icon. + ROTATE_CLOCKWISE_2 (IconName): The :code:`rotate-clockwise-2` icon. + ROTATE_DOT (IconName): The :code:`rotate-dot` icon. + ROTATE_RECTANGLE (IconName): The :code:`rotate-rectangle` icon. + ROUTE (IconName): The :code:`route` icon. + ROUTE_2 (IconName): The :code:`route-2` icon. + ROUTE_OFF (IconName): The :code:`route-off` icon. + ROUTER (IconName): The :code:`router` icon. + ROUTER_OFF (IconName): The :code:`router-off` icon. + ROW_INSERT_BOTTOM (IconName): The :code:`row-insert-bottom` icon. + ROW_INSERT_TOP (IconName): The :code:`row-insert-top` icon. + ROW_REMOVE (IconName): The :code:`row-remove` icon. + RSS (IconName): The :code:`rss` icon. + RUBBER_STAMP (IconName): The :code:`rubber-stamp` icon. + RUBBER_STAMP_OFF (IconName): The :code:`rubber-stamp-off` icon. + RULER (IconName): The :code:`ruler` icon. + RULER_2 (IconName): The :code:`ruler-2` icon. + RULER_2_OFF (IconName): The :code:`ruler-2-off` icon. + RULER_3 (IconName): The :code:`ruler-3` icon. + RULER_MEASURE (IconName): The :code:`ruler-measure` icon. + RULER_OFF (IconName): The :code:`ruler-off` icon. + RUN (IconName): The :code:`run` icon. + S_TURN_DOWN (IconName): The :code:`s-turn-down` icon. + S_TURN_LEFT (IconName): The :code:`s-turn-left` icon. + S_TURN_RIGHT (IconName): The :code:`s-turn-right` icon. + S_TURN_UP (IconName): The :code:`s-turn-up` icon. + SAILBOAT (IconName): The :code:`sailboat` icon. + SAILBOAT_2 (IconName): The :code:`sailboat-2` icon. + SAILBOAT_OFF (IconName): The :code:`sailboat-off` icon. + SALAD (IconName): The :code:`salad` icon. + SALT (IconName): The :code:`salt` icon. + SATELLITE (IconName): The :code:`satellite` icon. + SATELLITE_OFF (IconName): The :code:`satellite-off` icon. + SAUSAGE (IconName): The :code:`sausage` icon. + SCALE (IconName): The :code:`scale` icon. + SCALE_OFF (IconName): The :code:`scale-off` icon. + SCALE_OUTLINE (IconName): The :code:`scale-outline` icon. + SCALE_OUTLINE_OFF (IconName): The :code:`scale-outline-off` icon. + SCAN (IconName): The :code:`scan` icon. + SCAN_EYE (IconName): The :code:`scan-eye` icon. + SCHEMA (IconName): The :code:`schema` icon. + SCHEMA_OFF (IconName): The :code:`schema-off` icon. + SCHOOL (IconName): The :code:`school` icon. + SCHOOL_BELL (IconName): The :code:`school-bell` icon. + SCHOOL_OFF (IconName): The :code:`school-off` icon. + SCISSORS (IconName): The :code:`scissors` icon. + SCISSORS_OFF (IconName): The :code:`scissors-off` icon. + SCOOTER (IconName): The :code:`scooter` icon. + SCOOTER_ELECTRIC (IconName): The :code:`scooter-electric` icon. + SCOREBOARD (IconName): The :code:`scoreboard` icon. + SCREEN_SHARE (IconName): The :code:`screen-share` icon. + SCREEN_SHARE_OFF (IconName): The :code:`screen-share-off` icon. + SCREENSHOT (IconName): The :code:`screenshot` icon. + SCRIBBLE (IconName): The :code:`scribble` icon. + SCRIBBLE_OFF (IconName): The :code:`scribble-off` icon. + SCRIPT (IconName): The :code:`script` icon. + SCRIPT_MINUS (IconName): The :code:`script-minus` icon. + SCRIPT_PLUS (IconName): The :code:`script-plus` icon. + SCRIPT_X (IconName): The :code:`script-x` icon. + SCUBA_MASK (IconName): The :code:`scuba-mask` icon. + SCUBA_MASK_OFF (IconName): The :code:`scuba-mask-off` icon. + SDK (IconName): The :code:`sdk` icon. + SEARCH (IconName): The :code:`search` icon. + SEARCH_OFF (IconName): The :code:`search-off` icon. + SECTION (IconName): The :code:`section` icon. + SECTION_SIGN (IconName): The :code:`section-sign` icon. + SEEDING (IconName): The :code:`seeding` icon. + SEEDING_OFF (IconName): The :code:`seeding-off` icon. + SELECT (IconName): The :code:`select` icon. + SELECT_ALL (IconName): The :code:`select-all` icon. + SELECTOR (IconName): The :code:`selector` icon. + SEND (IconName): The :code:`send` icon. + SEND_OFF (IconName): The :code:`send-off` icon. + SEO (IconName): The :code:`seo` icon. + SEPARATOR (IconName): The :code:`separator` icon. + SEPARATOR_HORIZONTAL (IconName): The :code:`separator-horizontal` icon. + SEPARATOR_VERTICAL (IconName): The :code:`separator-vertical` icon. + SERVER (IconName): The :code:`server` icon. + SERVER_2 (IconName): The :code:`server-2` icon. + SERVER_BOLT (IconName): The :code:`server-bolt` icon. + SERVER_COG (IconName): The :code:`server-cog` icon. + SERVER_OFF (IconName): The :code:`server-off` icon. + SERVICEMARK (IconName): The :code:`servicemark` icon. + SETTINGS (IconName): The :code:`settings` icon. + SETTINGS_2 (IconName): The :code:`settings-2` icon. + SETTINGS_AUTOMATION (IconName): The :code:`settings-automation` icon. + SETTINGS_BOLT (IconName): The :code:`settings-bolt` icon. + SETTINGS_CANCEL (IconName): The :code:`settings-cancel` icon. + SETTINGS_CHECK (IconName): The :code:`settings-check` icon. + SETTINGS_CODE (IconName): The :code:`settings-code` icon. + SETTINGS_COG (IconName): The :code:`settings-cog` icon. + SETTINGS_DOLLAR (IconName): The :code:`settings-dollar` icon. + SETTINGS_DOWN (IconName): The :code:`settings-down` icon. + SETTINGS_EXCLAMATION (IconName): The :code:`settings-exclamation` icon. + SETTINGS_FILLED (IconName): The :code:`settings-filled` icon. + SETTINGS_HEART (IconName): The :code:`settings-heart` icon. + SETTINGS_MINUS (IconName): The :code:`settings-minus` icon. + SETTINGS_OFF (IconName): The :code:`settings-off` icon. + SETTINGS_PAUSE (IconName): The :code:`settings-pause` icon. + SETTINGS_PIN (IconName): The :code:`settings-pin` icon. + SETTINGS_PLUS (IconName): The :code:`settings-plus` icon. + SETTINGS_QUESTION (IconName): The :code:`settings-question` icon. + SETTINGS_SEARCH (IconName): The :code:`settings-search` icon. + SETTINGS_SHARE (IconName): The :code:`settings-share` icon. + SETTINGS_STAR (IconName): The :code:`settings-star` icon. + SETTINGS_UP (IconName): The :code:`settings-up` icon. + SETTINGS_X (IconName): The :code:`settings-x` icon. + SHADOW (IconName): The :code:`shadow` icon. + SHADOW_OFF (IconName): The :code:`shadow-off` icon. + SHAPE (IconName): The :code:`shape` icon. + SHAPE_2 (IconName): The :code:`shape-2` icon. + SHAPE_3 (IconName): The :code:`shape-3` icon. + SHAPE_OFF (IconName): The :code:`shape-off` icon. + SHARE (IconName): The :code:`share` icon. + SHARE_2 (IconName): The :code:`share-2` icon. + SHARE_3 (IconName): The :code:`share-3` icon. + SHARE_OFF (IconName): The :code:`share-off` icon. + SHI_JUMPING (IconName): The :code:`shi-jumping` icon. + SHIELD (IconName): The :code:`shield` icon. + SHIELD_BOLT (IconName): The :code:`shield-bolt` icon. + SHIELD_CANCEL (IconName): The :code:`shield-cancel` icon. + SHIELD_CHECK (IconName): The :code:`shield-check` icon. + SHIELD_CHECK_FILLED (IconName): The :code:`shield-check-filled` icon. + SHIELD_CHECKERED (IconName): The :code:`shield-checkered` icon. + SHIELD_CHECKERED_FILLED (IconName): The :code:`shield-checkered-filled` icon. + SHIELD_CHEVRON (IconName): The :code:`shield-chevron` icon. + SHIELD_CODE (IconName): The :code:`shield-code` icon. + SHIELD_COG (IconName): The :code:`shield-cog` icon. + SHIELD_DOLLAR (IconName): The :code:`shield-dollar` icon. + SHIELD_DOWN (IconName): The :code:`shield-down` icon. + SHIELD_EXCLAMATION (IconName): The :code:`shield-exclamation` icon. + SHIELD_FILLED (IconName): The :code:`shield-filled` icon. + SHIELD_HALF (IconName): The :code:`shield-half` icon. + SHIELD_HALF_FILLED (IconName): The :code:`shield-half-filled` icon. + SHIELD_HEART (IconName): The :code:`shield-heart` icon. + SHIELD_LOCK (IconName): The :code:`shield-lock` icon. + SHIELD_LOCK_FILLED (IconName): The :code:`shield-lock-filled` icon. + SHIELD_MINUS (IconName): The :code:`shield-minus` icon. + SHIELD_OFF (IconName): The :code:`shield-off` icon. + SHIELD_PAUSE (IconName): The :code:`shield-pause` icon. + SHIELD_PIN (IconName): The :code:`shield-pin` icon. + SHIELD_PLUS (IconName): The :code:`shield-plus` icon. + SHIELD_QUESTION (IconName): The :code:`shield-question` icon. + SHIELD_SEARCH (IconName): The :code:`shield-search` icon. + SHIELD_SHARE (IconName): The :code:`shield-share` icon. + SHIELD_STAR (IconName): The :code:`shield-star` icon. + SHIELD_UP (IconName): The :code:`shield-up` icon. + SHIELD_X (IconName): The :code:`shield-x` icon. + SHIP (IconName): The :code:`ship` icon. + SHIP_OFF (IconName): The :code:`ship-off` icon. + SHIRT (IconName): The :code:`shirt` icon. + SHIRT_FILLED (IconName): The :code:`shirt-filled` icon. + SHIRT_OFF (IconName): The :code:`shirt-off` icon. + SHIRT_SPORT (IconName): The :code:`shirt-sport` icon. + SHOE (IconName): The :code:`shoe` icon. + SHOE_OFF (IconName): The :code:`shoe-off` icon. + SHOPPING_BAG (IconName): The :code:`shopping-bag` icon. + SHOPPING_CART (IconName): The :code:`shopping-cart` icon. + SHOPPING_CART_DISCOUNT (IconName): The :code:`shopping-cart-discount` icon. + SHOPPING_CART_OFF (IconName): The :code:`shopping-cart-off` icon. + SHOPPING_CART_PLUS (IconName): The :code:`shopping-cart-plus` icon. + SHOPPING_CART_X (IconName): The :code:`shopping-cart-x` icon. + SHOVEL (IconName): The :code:`shovel` icon. + SHREDDER (IconName): The :code:`shredder` icon. + SIGN_LEFT (IconName): The :code:`sign-left` icon. + SIGN_LEFT_FILLED (IconName): The :code:`sign-left-filled` icon. + SIGN_RIGHT (IconName): The :code:`sign-right` icon. + SIGN_RIGHT_FILLED (IconName): The :code:`sign-right-filled` icon. + SIGNAL_2G (IconName): The :code:`signal-2g` icon. + SIGNAL_3G (IconName): The :code:`signal-3g` icon. + SIGNAL_4G (IconName): The :code:`signal-4g` icon. + SIGNAL_4G_PLUS (IconName): The :code:`signal-4g-plus` icon. + SIGNAL_5G (IconName): The :code:`signal-5g` icon. + SIGNAL_6G (IconName): The :code:`signal-6g` icon. + SIGNAL_E (IconName): The :code:`signal-e` icon. + SIGNAL_G (IconName): The :code:`signal-g` icon. + SIGNAL_H (IconName): The :code:`signal-h` icon. + SIGNAL_H_PLUS (IconName): The :code:`signal-h-plus` icon. + SIGNAL_LTE (IconName): The :code:`signal-lte` icon. + SIGNATURE (IconName): The :code:`signature` icon. + SIGNATURE_OFF (IconName): The :code:`signature-off` icon. + SITEMAP (IconName): The :code:`sitemap` icon. + SITEMAP_OFF (IconName): The :code:`sitemap-off` icon. + SKATEBOARD (IconName): The :code:`skateboard` icon. + SKATEBOARD_OFF (IconName): The :code:`skateboard-off` icon. + SKATEBOARDING (IconName): The :code:`skateboarding` icon. + SKULL (IconName): The :code:`skull` icon. + SLASH (IconName): The :code:`slash` icon. + SLASHES (IconName): The :code:`slashes` icon. + SLEIGH (IconName): The :code:`sleigh` icon. + SLICE (IconName): The :code:`slice` icon. + SLIDESHOW (IconName): The :code:`slideshow` icon. + SMART_HOME (IconName): The :code:`smart-home` icon. + SMART_HOME_OFF (IconName): The :code:`smart-home-off` icon. + SMOKING (IconName): The :code:`smoking` icon. + SMOKING_NO (IconName): The :code:`smoking-no` icon. + SNOWFLAKE (IconName): The :code:`snowflake` icon. + SNOWFLAKE_OFF (IconName): The :code:`snowflake-off` icon. + SNOWMAN (IconName): The :code:`snowman` icon. + SOCCER_FIELD (IconName): The :code:`soccer-field` icon. + SOCIAL (IconName): The :code:`social` icon. + SOCIAL_OFF (IconName): The :code:`social-off` icon. + SOCK (IconName): The :code:`sock` icon. + SOFA (IconName): The :code:`sofa` icon. + SOFA_OFF (IconName): The :code:`sofa-off` icon. + SOLAR_PANEL (IconName): The :code:`solar-panel` icon. + SOLAR_PANEL_2 (IconName): The :code:`solar-panel-2` icon. + SORT_0_9 (IconName): The :code:`sort-0-9` icon. + SORT_9_0 (IconName): The :code:`sort-9-0` icon. + SORT_A_Z (IconName): The :code:`sort-a-z` icon. + SORT_ASCENDING (IconName): The :code:`sort-ascending` icon. + SORT_ASCENDING_2 (IconName): The :code:`sort-ascending-2` icon. + SORT_ASCENDING_LETTERS (IconName): The :code:`sort-ascending-letters` icon. + SORT_ASCENDING_NUMBERS (IconName): The :code:`sort-ascending-numbers` icon. + SORT_DESCENDING (IconName): The :code:`sort-descending` icon. + SORT_DESCENDING_2 (IconName): The :code:`sort-descending-2` icon. + SORT_DESCENDING_LETTERS (IconName): The :code:`sort-descending-letters` icon. + SORT_DESCENDING_NUMBERS (IconName): The :code:`sort-descending-numbers` icon. + SORT_Z_A (IconName): The :code:`sort-z-a` icon. + SOS (IconName): The :code:`sos` icon. + SOUP (IconName): The :code:`soup` icon. + SOUP_OFF (IconName): The :code:`soup-off` icon. + SOURCE_CODE (IconName): The :code:`source-code` icon. + SPACE (IconName): The :code:`space` icon. + SPACE_OFF (IconName): The :code:`space-off` icon. + SPACING_HORIZONTAL (IconName): The :code:`spacing-horizontal` icon. + SPACING_VERTICAL (IconName): The :code:`spacing-vertical` icon. + SPADE (IconName): The :code:`spade` icon. + SPADE_FILLED (IconName): The :code:`spade-filled` icon. + SPARKLES (IconName): The :code:`sparkles` icon. + SPEAKERPHONE (IconName): The :code:`speakerphone` icon. + SPEEDBOAT (IconName): The :code:`speedboat` icon. + SPHERE (IconName): The :code:`sphere` icon. + SPHERE_OFF (IconName): The :code:`sphere-off` icon. + SPHERE_PLUS (IconName): The :code:`sphere-plus` icon. + SPIDER (IconName): The :code:`spider` icon. + SPIRAL (IconName): The :code:`spiral` icon. + SPIRAL_OFF (IconName): The :code:`spiral-off` icon. + SPORT_BILLARD (IconName): The :code:`sport-billard` icon. + SPRAY (IconName): The :code:`spray` icon. + SPY (IconName): The :code:`spy` icon. + SPY_OFF (IconName): The :code:`spy-off` icon. + SQL (IconName): The :code:`sql` icon. + SQUARE (IconName): The :code:`square` icon. + SQUARE_0_FILLED (IconName): The :code:`square-0-filled` icon. + SQUARE_1_FILLED (IconName): The :code:`square-1-filled` icon. + SQUARE_2_FILLED (IconName): The :code:`square-2-filled` icon. + SQUARE_3_FILLED (IconName): The :code:`square-3-filled` icon. + SQUARE_4_FILLED (IconName): The :code:`square-4-filled` icon. + SQUARE_5_FILLED (IconName): The :code:`square-5-filled` icon. + SQUARE_6_FILLED (IconName): The :code:`square-6-filled` icon. + SQUARE_7_FILLED (IconName): The :code:`square-7-filled` icon. + SQUARE_8_FILLED (IconName): The :code:`square-8-filled` icon. + SQUARE_9_FILLED (IconName): The :code:`square-9-filled` icon. + SQUARE_ARROW_DOWN (IconName): The :code:`square-arrow-down` icon. + SQUARE_ARROW_LEFT (IconName): The :code:`square-arrow-left` icon. + SQUARE_ARROW_RIGHT (IconName): The :code:`square-arrow-right` icon. + SQUARE_ARROW_UP (IconName): The :code:`square-arrow-up` icon. + SQUARE_ASTERISK (IconName): The :code:`square-asterisk` icon. + SQUARE_CHECK (IconName): The :code:`square-check` icon. + SQUARE_CHECK_FILLED (IconName): The :code:`square-check-filled` icon. + SQUARE_CHEVRON_DOWN (IconName): The :code:`square-chevron-down` icon. + SQUARE_CHEVRON_LEFT (IconName): The :code:`square-chevron-left` icon. + SQUARE_CHEVRON_RIGHT (IconName): The :code:`square-chevron-right` icon. + SQUARE_CHEVRON_UP (IconName): The :code:`square-chevron-up` icon. + SQUARE_CHEVRONS_DOWN (IconName): The :code:`square-chevrons-down` icon. + SQUARE_CHEVRONS_LEFT (IconName): The :code:`square-chevrons-left` icon. + SQUARE_CHEVRONS_RIGHT (IconName): The :code:`square-chevrons-right` icon. + SQUARE_CHEVRONS_UP (IconName): The :code:`square-chevrons-up` icon. + SQUARE_DOT (IconName): The :code:`square-dot` icon. + SQUARE_F0 (IconName): The :code:`square-f0` icon. + SQUARE_F0_FILLED (IconName): The :code:`square-f0-filled` icon. + SQUARE_F1 (IconName): The :code:`square-f1` icon. + SQUARE_F1_FILLED (IconName): The :code:`square-f1-filled` icon. + SQUARE_F2 (IconName): The :code:`square-f2` icon. + SQUARE_F2_FILLED (IconName): The :code:`square-f2-filled` icon. + SQUARE_F3 (IconName): The :code:`square-f3` icon. + SQUARE_F3_FILLED (IconName): The :code:`square-f3-filled` icon. + SQUARE_F4 (IconName): The :code:`square-f4` icon. + SQUARE_F4_FILLED (IconName): The :code:`square-f4-filled` icon. + SQUARE_F5 (IconName): The :code:`square-f5` icon. + SQUARE_F5_FILLED (IconName): The :code:`square-f5-filled` icon. + SQUARE_F6 (IconName): The :code:`square-f6` icon. + SQUARE_F6_FILLED (IconName): The :code:`square-f6-filled` icon. + SQUARE_F7 (IconName): The :code:`square-f7` icon. + SQUARE_F7_FILLED (IconName): The :code:`square-f7-filled` icon. + SQUARE_F8 (IconName): The :code:`square-f8` icon. + SQUARE_F8_FILLED (IconName): The :code:`square-f8-filled` icon. + SQUARE_F9 (IconName): The :code:`square-f9` icon. + SQUARE_F9_FILLED (IconName): The :code:`square-f9-filled` icon. + SQUARE_FORBID (IconName): The :code:`square-forbid` icon. + SQUARE_FORBID_2 (IconName): The :code:`square-forbid-2` icon. + SQUARE_HALF (IconName): The :code:`square-half` icon. + SQUARE_KEY (IconName): The :code:`square-key` icon. + SQUARE_LETTER_A (IconName): The :code:`square-letter-a` icon. + SQUARE_LETTER_B (IconName): The :code:`square-letter-b` icon. + SQUARE_LETTER_C (IconName): The :code:`square-letter-c` icon. + SQUARE_LETTER_D (IconName): The :code:`square-letter-d` icon. + SQUARE_LETTER_E (IconName): The :code:`square-letter-e` icon. + SQUARE_LETTER_F (IconName): The :code:`square-letter-f` icon. + SQUARE_LETTER_G (IconName): The :code:`square-letter-g` icon. + SQUARE_LETTER_H (IconName): The :code:`square-letter-h` icon. + SQUARE_LETTER_I (IconName): The :code:`square-letter-i` icon. + SQUARE_LETTER_J (IconName): The :code:`square-letter-j` icon. + SQUARE_LETTER_K (IconName): The :code:`square-letter-k` icon. + SQUARE_LETTER_L (IconName): The :code:`square-letter-l` icon. + SQUARE_LETTER_M (IconName): The :code:`square-letter-m` icon. + SQUARE_LETTER_N (IconName): The :code:`square-letter-n` icon. + SQUARE_LETTER_O (IconName): The :code:`square-letter-o` icon. + SQUARE_LETTER_P (IconName): The :code:`square-letter-p` icon. + SQUARE_LETTER_Q (IconName): The :code:`square-letter-q` icon. + SQUARE_LETTER_R (IconName): The :code:`square-letter-r` icon. + SQUARE_LETTER_S (IconName): The :code:`square-letter-s` icon. + SQUARE_LETTER_T (IconName): The :code:`square-letter-t` icon. + SQUARE_LETTER_U (IconName): The :code:`square-letter-u` icon. + SQUARE_LETTER_V (IconName): The :code:`square-letter-v` icon. + SQUARE_LETTER_W (IconName): The :code:`square-letter-w` icon. + SQUARE_LETTER_X (IconName): The :code:`square-letter-x` icon. + SQUARE_LETTER_Y (IconName): The :code:`square-letter-y` icon. + SQUARE_LETTER_Z (IconName): The :code:`square-letter-z` icon. + SQUARE_MINUS (IconName): The :code:`square-minus` icon. + SQUARE_NUMBER_0 (IconName): The :code:`square-number-0` icon. + SQUARE_NUMBER_1 (IconName): The :code:`square-number-1` icon. + SQUARE_NUMBER_2 (IconName): The :code:`square-number-2` icon. + SQUARE_NUMBER_3 (IconName): The :code:`square-number-3` icon. + SQUARE_NUMBER_4 (IconName): The :code:`square-number-4` icon. + SQUARE_NUMBER_5 (IconName): The :code:`square-number-5` icon. + SQUARE_NUMBER_6 (IconName): The :code:`square-number-6` icon. + SQUARE_NUMBER_7 (IconName): The :code:`square-number-7` icon. + SQUARE_NUMBER_8 (IconName): The :code:`square-number-8` icon. + SQUARE_NUMBER_9 (IconName): The :code:`square-number-9` icon. + SQUARE_OFF (IconName): The :code:`square-off` icon. + SQUARE_PLUS (IconName): The :code:`square-plus` icon. + SQUARE_ROOT (IconName): The :code:`square-root` icon. + SQUARE_ROOT_2 (IconName): The :code:`square-root-2` icon. + SQUARE_ROTATED (IconName): The :code:`square-rotated` icon. + SQUARE_ROTATED_FILLED (IconName): The :code:`square-rotated-filled` icon. + SQUARE_ROTATED_FORBID (IconName): The :code:`square-rotated-forbid` icon. + SQUARE_ROTATED_FORBID_2 (IconName): The :code:`square-rotated-forbid-2` icon. + SQUARE_ROTATED_OFF (IconName): The :code:`square-rotated-off` icon. + SQUARE_ROUNDED (IconName): The :code:`square-rounded` icon. + SQUARE_ROUNDED_ARROW_DOWN (IconName): The :code:`square-rounded-arrow-down` icon. + SQUARE_ROUNDED_ARROW_DOWN_FILLED (IconName): The :code:`square-rounded-arrow-down-filled` icon. + SQUARE_ROUNDED_ARROW_LEFT (IconName): The :code:`square-rounded-arrow-left` icon. + SQUARE_ROUNDED_ARROW_LEFT_FILLED (IconName): The :code:`square-rounded-arrow-left-filled` icon. + SQUARE_ROUNDED_ARROW_RIGHT (IconName): The :code:`square-rounded-arrow-right` icon. + SQUARE_ROUNDED_ARROW_RIGHT_FILLED (IconName): The :code:`square-rounded-arrow-right-filled` icon. + SQUARE_ROUNDED_ARROW_UP (IconName): The :code:`square-rounded-arrow-up` icon. + SQUARE_ROUNDED_ARROW_UP_FILLED (IconName): The :code:`square-rounded-arrow-up-filled` icon. + SQUARE_ROUNDED_CHECK (IconName): The :code:`square-rounded-check` icon. + SQUARE_ROUNDED_CHECK_FILLED (IconName): The :code:`square-rounded-check-filled` icon. + SQUARE_ROUNDED_CHEVRON_DOWN (IconName): The :code:`square-rounded-chevron-down` icon. + SQUARE_ROUNDED_CHEVRON_DOWN_FILLED (IconName): The :code:`square-rounded-chevron-down-filled` icon. + SQUARE_ROUNDED_CHEVRON_LEFT (IconName): The :code:`square-rounded-chevron-left` icon. + SQUARE_ROUNDED_CHEVRON_LEFT_FILLED (IconName): The :code:`square-rounded-chevron-left-filled` icon. + SQUARE_ROUNDED_CHEVRON_RIGHT (IconName): The :code:`square-rounded-chevron-right` icon. + SQUARE_ROUNDED_CHEVRON_RIGHT_FILLED (IconName): The :code:`square-rounded-chevron-right-filled` icon. + SQUARE_ROUNDED_CHEVRON_UP (IconName): The :code:`square-rounded-chevron-up` icon. + SQUARE_ROUNDED_CHEVRON_UP_FILLED (IconName): The :code:`square-rounded-chevron-up-filled` icon. + SQUARE_ROUNDED_CHEVRONS_DOWN (IconName): The :code:`square-rounded-chevrons-down` icon. + SQUARE_ROUNDED_CHEVRONS_DOWN_FILLED (IconName): The :code:`square-rounded-chevrons-down-filled` icon. + SQUARE_ROUNDED_CHEVRONS_LEFT (IconName): The :code:`square-rounded-chevrons-left` icon. + SQUARE_ROUNDED_CHEVRONS_LEFT_FILLED (IconName): The :code:`square-rounded-chevrons-left-filled` icon. + SQUARE_ROUNDED_CHEVRONS_RIGHT (IconName): The :code:`square-rounded-chevrons-right` icon. + SQUARE_ROUNDED_CHEVRONS_RIGHT_FILLED (IconName): The :code:`square-rounded-chevrons-right-filled` icon. + SQUARE_ROUNDED_CHEVRONS_UP (IconName): The :code:`square-rounded-chevrons-up` icon. + SQUARE_ROUNDED_CHEVRONS_UP_FILLED (IconName): The :code:`square-rounded-chevrons-up-filled` icon. + SQUARE_ROUNDED_FILLED (IconName): The :code:`square-rounded-filled` icon. + SQUARE_ROUNDED_LETTER_A (IconName): The :code:`square-rounded-letter-a` icon. + SQUARE_ROUNDED_LETTER_B (IconName): The :code:`square-rounded-letter-b` icon. + SQUARE_ROUNDED_LETTER_C (IconName): The :code:`square-rounded-letter-c` icon. + SQUARE_ROUNDED_LETTER_D (IconName): The :code:`square-rounded-letter-d` icon. + SQUARE_ROUNDED_LETTER_E (IconName): The :code:`square-rounded-letter-e` icon. + SQUARE_ROUNDED_LETTER_F (IconName): The :code:`square-rounded-letter-f` icon. + SQUARE_ROUNDED_LETTER_G (IconName): The :code:`square-rounded-letter-g` icon. + SQUARE_ROUNDED_LETTER_H (IconName): The :code:`square-rounded-letter-h` icon. + SQUARE_ROUNDED_LETTER_I (IconName): The :code:`square-rounded-letter-i` icon. + SQUARE_ROUNDED_LETTER_J (IconName): The :code:`square-rounded-letter-j` icon. + SQUARE_ROUNDED_LETTER_K (IconName): The :code:`square-rounded-letter-k` icon. + SQUARE_ROUNDED_LETTER_L (IconName): The :code:`square-rounded-letter-l` icon. + SQUARE_ROUNDED_LETTER_M (IconName): The :code:`square-rounded-letter-m` icon. + SQUARE_ROUNDED_LETTER_N (IconName): The :code:`square-rounded-letter-n` icon. + SQUARE_ROUNDED_LETTER_O (IconName): The :code:`square-rounded-letter-o` icon. + SQUARE_ROUNDED_LETTER_P (IconName): The :code:`square-rounded-letter-p` icon. + SQUARE_ROUNDED_LETTER_Q (IconName): The :code:`square-rounded-letter-q` icon. + SQUARE_ROUNDED_LETTER_R (IconName): The :code:`square-rounded-letter-r` icon. + SQUARE_ROUNDED_LETTER_S (IconName): The :code:`square-rounded-letter-s` icon. + SQUARE_ROUNDED_LETTER_T (IconName): The :code:`square-rounded-letter-t` icon. + SQUARE_ROUNDED_LETTER_U (IconName): The :code:`square-rounded-letter-u` icon. + SQUARE_ROUNDED_LETTER_V (IconName): The :code:`square-rounded-letter-v` icon. + SQUARE_ROUNDED_LETTER_W (IconName): The :code:`square-rounded-letter-w` icon. + SQUARE_ROUNDED_LETTER_X (IconName): The :code:`square-rounded-letter-x` icon. + SQUARE_ROUNDED_LETTER_Y (IconName): The :code:`square-rounded-letter-y` icon. + SQUARE_ROUNDED_LETTER_Z (IconName): The :code:`square-rounded-letter-z` icon. + SQUARE_ROUNDED_MINUS (IconName): The :code:`square-rounded-minus` icon. + SQUARE_ROUNDED_NUMBER_0 (IconName): The :code:`square-rounded-number-0` icon. + SQUARE_ROUNDED_NUMBER_0_FILLED (IconName): The :code:`square-rounded-number-0-filled` icon. + SQUARE_ROUNDED_NUMBER_1 (IconName): The :code:`square-rounded-number-1` icon. + SQUARE_ROUNDED_NUMBER_1_FILLED (IconName): The :code:`square-rounded-number-1-filled` icon. + SQUARE_ROUNDED_NUMBER_2 (IconName): The :code:`square-rounded-number-2` icon. + SQUARE_ROUNDED_NUMBER_2_FILLED (IconName): The :code:`square-rounded-number-2-filled` icon. + SQUARE_ROUNDED_NUMBER_3 (IconName): The :code:`square-rounded-number-3` icon. + SQUARE_ROUNDED_NUMBER_3_FILLED (IconName): The :code:`square-rounded-number-3-filled` icon. + SQUARE_ROUNDED_NUMBER_4 (IconName): The :code:`square-rounded-number-4` icon. + SQUARE_ROUNDED_NUMBER_4_FILLED (IconName): The :code:`square-rounded-number-4-filled` icon. + SQUARE_ROUNDED_NUMBER_5 (IconName): The :code:`square-rounded-number-5` icon. + SQUARE_ROUNDED_NUMBER_5_FILLED (IconName): The :code:`square-rounded-number-5-filled` icon. + SQUARE_ROUNDED_NUMBER_6 (IconName): The :code:`square-rounded-number-6` icon. + SQUARE_ROUNDED_NUMBER_6_FILLED (IconName): The :code:`square-rounded-number-6-filled` icon. + SQUARE_ROUNDED_NUMBER_7 (IconName): The :code:`square-rounded-number-7` icon. + SQUARE_ROUNDED_NUMBER_7_FILLED (IconName): The :code:`square-rounded-number-7-filled` icon. + SQUARE_ROUNDED_NUMBER_8 (IconName): The :code:`square-rounded-number-8` icon. + SQUARE_ROUNDED_NUMBER_8_FILLED (IconName): The :code:`square-rounded-number-8-filled` icon. + SQUARE_ROUNDED_NUMBER_9 (IconName): The :code:`square-rounded-number-9` icon. + SQUARE_ROUNDED_NUMBER_9_FILLED (IconName): The :code:`square-rounded-number-9-filled` icon. + SQUARE_ROUNDED_PLUS (IconName): The :code:`square-rounded-plus` icon. + SQUARE_ROUNDED_PLUS_FILLED (IconName): The :code:`square-rounded-plus-filled` icon. + SQUARE_ROUNDED_X (IconName): The :code:`square-rounded-x` icon. + SQUARE_ROUNDED_X_FILLED (IconName): The :code:`square-rounded-x-filled` icon. + SQUARE_TOGGLE (IconName): The :code:`square-toggle` icon. + SQUARE_TOGGLE_HORIZONTAL (IconName): The :code:`square-toggle-horizontal` icon. + SQUARE_X (IconName): The :code:`square-x` icon. + SQUARES_DIAGONAL (IconName): The :code:`squares-diagonal` icon. + SQUARES_FILLED (IconName): The :code:`squares-filled` icon. + STACK (IconName): The :code:`stack` icon. + STACK_2 (IconName): The :code:`stack-2` icon. + STACK_3 (IconName): The :code:`stack-3` icon. + STACK_POP (IconName): The :code:`stack-pop` icon. + STACK_PUSH (IconName): The :code:`stack-push` icon. + STAIRS (IconName): The :code:`stairs` icon. + STAIRS_DOWN (IconName): The :code:`stairs-down` icon. + STAIRS_UP (IconName): The :code:`stairs-up` icon. + STAR (IconName): The :code:`star` icon. + STAR_FILLED (IconName): The :code:`star-filled` icon. + STAR_HALF (IconName): The :code:`star-half` icon. + STAR_HALF_FILLED (IconName): The :code:`star-half-filled` icon. + STAR_OFF (IconName): The :code:`star-off` icon. + STARS (IconName): The :code:`stars` icon. + STARS_FILLED (IconName): The :code:`stars-filled` icon. + STARS_OFF (IconName): The :code:`stars-off` icon. + STATUS_CHANGE (IconName): The :code:`status-change` icon. + STEAM (IconName): The :code:`steam` icon. + STEERING_WHEEL (IconName): The :code:`steering-wheel` icon. + STEERING_WHEEL_OFF (IconName): The :code:`steering-wheel-off` icon. + STEP_INTO (IconName): The :code:`step-into` icon. + STEP_OUT (IconName): The :code:`step-out` icon. + STEREO_GLASSES (IconName): The :code:`stereo-glasses` icon. + STETHOSCOPE (IconName): The :code:`stethoscope` icon. + STETHOSCOPE_OFF (IconName): The :code:`stethoscope-off` icon. + STICKER (IconName): The :code:`sticker` icon. + STORM (IconName): The :code:`storm` icon. + STORM_OFF (IconName): The :code:`storm-off` icon. + STRETCHING (IconName): The :code:`stretching` icon. + STRETCHING_2 (IconName): The :code:`stretching-2` icon. + STRIKETHROUGH (IconName): The :code:`strikethrough` icon. + SUBMARINE (IconName): The :code:`submarine` icon. + SUBSCRIPT (IconName): The :code:`subscript` icon. + SUBTASK (IconName): The :code:`subtask` icon. + SUM (IconName): The :code:`sum` icon. + SUM_OFF (IconName): The :code:`sum-off` icon. + SUN (IconName): The :code:`sun` icon. + SUN_FILLED (IconName): The :code:`sun-filled` icon. + SUN_HIGH (IconName): The :code:`sun-high` icon. + SUN_LOW (IconName): The :code:`sun-low` icon. + SUN_MOON (IconName): The :code:`sun-moon` icon. + SUN_OFF (IconName): The :code:`sun-off` icon. + SUN_WIND (IconName): The :code:`sun-wind` icon. + SUNGLASSES (IconName): The :code:`sunglasses` icon. + SUNRISE (IconName): The :code:`sunrise` icon. + SUNSET (IconName): The :code:`sunset` icon. + SUNSET_2 (IconName): The :code:`sunset-2` icon. + SUPERSCRIPT (IconName): The :code:`superscript` icon. + SVG (IconName): The :code:`svg` icon. + SWIMMING (IconName): The :code:`swimming` icon. + SWIPE (IconName): The :code:`swipe` icon. + SWITCH (IconName): The :code:`switch` icon. + SWITCH_2 (IconName): The :code:`switch-2` icon. + SWITCH_3 (IconName): The :code:`switch-3` icon. + SWITCH_HORIZONTAL (IconName): The :code:`switch-horizontal` icon. + SWITCH_VERTICAL (IconName): The :code:`switch-vertical` icon. + SWORD (IconName): The :code:`sword` icon. + SWORD_OFF (IconName): The :code:`sword-off` icon. + SWORDS (IconName): The :code:`swords` icon. + TABLE (IconName): The :code:`table` icon. + TABLE_ALIAS (IconName): The :code:`table-alias` icon. + TABLE_COLUMN (IconName): The :code:`table-column` icon. + TABLE_DOWN (IconName): The :code:`table-down` icon. + TABLE_EXPORT (IconName): The :code:`table-export` icon. + TABLE_FILLED (IconName): The :code:`table-filled` icon. + TABLE_HEART (IconName): The :code:`table-heart` icon. + TABLE_IMPORT (IconName): The :code:`table-import` icon. + TABLE_MINUS (IconName): The :code:`table-minus` icon. + TABLE_OFF (IconName): The :code:`table-off` icon. + TABLE_OPTIONS (IconName): The :code:`table-options` icon. + TABLE_PLUS (IconName): The :code:`table-plus` icon. + TABLE_ROW (IconName): The :code:`table-row` icon. + TABLE_SHARE (IconName): The :code:`table-share` icon. + TABLE_SHORTCUT (IconName): The :code:`table-shortcut` icon. + TAG (IconName): The :code:`tag` icon. + TAG_OFF (IconName): The :code:`tag-off` icon. + TAGS (IconName): The :code:`tags` icon. + TAGS_OFF (IconName): The :code:`tags-off` icon. + TALLYMARK_1 (IconName): The :code:`tallymark-1` icon. + TALLYMARK_2 (IconName): The :code:`tallymark-2` icon. + TALLYMARK_3 (IconName): The :code:`tallymark-3` icon. + TALLYMARK_4 (IconName): The :code:`tallymark-4` icon. + TALLYMARKS (IconName): The :code:`tallymarks` icon. + TANK (IconName): The :code:`tank` icon. + TARGET (IconName): The :code:`target` icon. + TARGET_ARROW (IconName): The :code:`target-arrow` icon. + TARGET_OFF (IconName): The :code:`target-off` icon. + TEAPOT (IconName): The :code:`teapot` icon. + TELESCOPE (IconName): The :code:`telescope` icon. + TELESCOPE_OFF (IconName): The :code:`telescope-off` icon. + TEMPERATURE (IconName): The :code:`temperature` icon. + TEMPERATURE_CELSIUS (IconName): The :code:`temperature-celsius` icon. + TEMPERATURE_FAHRENHEIT (IconName): The :code:`temperature-fahrenheit` icon. + TEMPERATURE_MINUS (IconName): The :code:`temperature-minus` icon. + TEMPERATURE_OFF (IconName): The :code:`temperature-off` icon. + TEMPERATURE_PLUS (IconName): The :code:`temperature-plus` icon. + TEMPLATE (IconName): The :code:`template` icon. + TEMPLATE_OFF (IconName): The :code:`template-off` icon. + TENT (IconName): The :code:`tent` icon. + TENT_OFF (IconName): The :code:`tent-off` icon. + TERMINAL (IconName): The :code:`terminal` icon. + TERMINAL_2 (IconName): The :code:`terminal-2` icon. + TEST_PIPE (IconName): The :code:`test-pipe` icon. + TEST_PIPE_2 (IconName): The :code:`test-pipe-2` icon. + TEST_PIPE_OFF (IconName): The :code:`test-pipe-off` icon. + TEX (IconName): The :code:`tex` icon. + TEXT_CAPTION (IconName): The :code:`text-caption` icon. + TEXT_COLOR (IconName): The :code:`text-color` icon. + TEXT_DECREASE (IconName): The :code:`text-decrease` icon. + TEXT_DIRECTION_LTR (IconName): The :code:`text-direction-ltr` icon. + TEXT_DIRECTION_RTL (IconName): The :code:`text-direction-rtl` icon. + TEXT_INCREASE (IconName): The :code:`text-increase` icon. + TEXT_ORIENTATION (IconName): The :code:`text-orientation` icon. + TEXT_PLUS (IconName): The :code:`text-plus` icon. + TEXT_RECOGNITION (IconName): The :code:`text-recognition` icon. + TEXT_RESIZE (IconName): The :code:`text-resize` icon. + TEXT_SIZE (IconName): The :code:`text-size` icon. + TEXT_SPELLCHECK (IconName): The :code:`text-spellcheck` icon. + TEXT_WRAP (IconName): The :code:`text-wrap` icon. + TEXT_WRAP_DISABLED (IconName): The :code:`text-wrap-disabled` icon. + TEXTURE (IconName): The :code:`texture` icon. + THEATER (IconName): The :code:`theater` icon. + THERMOMETER (IconName): The :code:`thermometer` icon. + THUMB_DOWN (IconName): The :code:`thumb-down` icon. + THUMB_DOWN_FILLED (IconName): The :code:`thumb-down-filled` icon. + THUMB_DOWN_OFF (IconName): The :code:`thumb-down-off` icon. + THUMB_UP (IconName): The :code:`thumb-up` icon. + THUMB_UP_FILLED (IconName): The :code:`thumb-up-filled` icon. + THUMB_UP_OFF (IconName): The :code:`thumb-up-off` icon. + TIC_TAC (IconName): The :code:`tic-tac` icon. + TICKET (IconName): The :code:`ticket` icon. + TICKET_OFF (IconName): The :code:`ticket-off` icon. + TIE (IconName): The :code:`tie` icon. + TILDE (IconName): The :code:`tilde` icon. + TILT_SHIFT (IconName): The :code:`tilt-shift` icon. + TILT_SHIFT_OFF (IconName): The :code:`tilt-shift-off` icon. + TIME_DURATION_0 (IconName): The :code:`time-duration-0` icon. + TIME_DURATION_10 (IconName): The :code:`time-duration-10` icon. + TIME_DURATION_15 (IconName): The :code:`time-duration-15` icon. + TIME_DURATION_30 (IconName): The :code:`time-duration-30` icon. + TIME_DURATION_45 (IconName): The :code:`time-duration-45` icon. + TIME_DURATION_5 (IconName): The :code:`time-duration-5` icon. + TIME_DURATION_60 (IconName): The :code:`time-duration-60` icon. + TIME_DURATION_90 (IconName): The :code:`time-duration-90` icon. + TIME_DURATION_OFF (IconName): The :code:`time-duration-off` icon. + TIMELINE (IconName): The :code:`timeline` icon. + TIMELINE_EVENT (IconName): The :code:`timeline-event` icon. + TIMELINE_EVENT_EXCLAMATION (IconName): The :code:`timeline-event-exclamation` icon. + TIMELINE_EVENT_MINUS (IconName): The :code:`timeline-event-minus` icon. + TIMELINE_EVENT_PLUS (IconName): The :code:`timeline-event-plus` icon. + TIMELINE_EVENT_TEXT (IconName): The :code:`timeline-event-text` icon. + TIMELINE_EVENT_X (IconName): The :code:`timeline-event-x` icon. + TIR (IconName): The :code:`tir` icon. + TOGGLE_LEFT (IconName): The :code:`toggle-left` icon. + TOGGLE_RIGHT (IconName): The :code:`toggle-right` icon. + TOILET_PAPER (IconName): The :code:`toilet-paper` icon. + TOILET_PAPER_OFF (IconName): The :code:`toilet-paper-off` icon. + TOML (IconName): The :code:`toml` icon. + TOOL (IconName): The :code:`tool` icon. + TOOLS (IconName): The :code:`tools` icon. + TOOLS_KITCHEN (IconName): The :code:`tools-kitchen` icon. + TOOLS_KITCHEN_2 (IconName): The :code:`tools-kitchen-2` icon. + TOOLS_KITCHEN_2_OFF (IconName): The :code:`tools-kitchen-2-off` icon. + TOOLS_KITCHEN_OFF (IconName): The :code:`tools-kitchen-off` icon. + TOOLS_OFF (IconName): The :code:`tools-off` icon. + TOOLTIP (IconName): The :code:`tooltip` icon. + TOPOLOGY_BUS (IconName): The :code:`topology-bus` icon. + TOPOLOGY_COMPLEX (IconName): The :code:`topology-complex` icon. + TOPOLOGY_FULL (IconName): The :code:`topology-full` icon. + TOPOLOGY_FULL_HIERARCHY (IconName): The :code:`topology-full-hierarchy` icon. + TOPOLOGY_RING (IconName): The :code:`topology-ring` icon. + TOPOLOGY_RING_2 (IconName): The :code:`topology-ring-2` icon. + TOPOLOGY_RING_3 (IconName): The :code:`topology-ring-3` icon. + TOPOLOGY_STAR (IconName): The :code:`topology-star` icon. + TOPOLOGY_STAR_2 (IconName): The :code:`topology-star-2` icon. + TOPOLOGY_STAR_3 (IconName): The :code:`topology-star-3` icon. + TOPOLOGY_STAR_RING (IconName): The :code:`topology-star-ring` icon. + TOPOLOGY_STAR_RING_2 (IconName): The :code:`topology-star-ring-2` icon. + TOPOLOGY_STAR_RING_3 (IconName): The :code:`topology-star-ring-3` icon. + TORII (IconName): The :code:`torii` icon. + TORNADO (IconName): The :code:`tornado` icon. + TOURNAMENT (IconName): The :code:`tournament` icon. + TOWER (IconName): The :code:`tower` icon. + TOWER_OFF (IconName): The :code:`tower-off` icon. + TRACK (IconName): The :code:`track` icon. + TRACTOR (IconName): The :code:`tractor` icon. + TRADEMARK (IconName): The :code:`trademark` icon. + TRAFFIC_CONE (IconName): The :code:`traffic-cone` icon. + TRAFFIC_CONE_OFF (IconName): The :code:`traffic-cone-off` icon. + TRAFFIC_LIGHTS (IconName): The :code:`traffic-lights` icon. + TRAFFIC_LIGHTS_OFF (IconName): The :code:`traffic-lights-off` icon. + TRAIN (IconName): The :code:`train` icon. + TRANSFER_IN (IconName): The :code:`transfer-in` icon. + TRANSFER_OUT (IconName): The :code:`transfer-out` icon. + TRANSFORM (IconName): The :code:`transform` icon. + TRANSFORM_FILLED (IconName): The :code:`transform-filled` icon. + TRANSITION_BOTTOM (IconName): The :code:`transition-bottom` icon. + TRANSITION_LEFT (IconName): The :code:`transition-left` icon. + TRANSITION_RIGHT (IconName): The :code:`transition-right` icon. + TRANSITION_TOP (IconName): The :code:`transition-top` icon. + TRASH (IconName): The :code:`trash` icon. + TRASH_FILLED (IconName): The :code:`trash-filled` icon. + TRASH_OFF (IconName): The :code:`trash-off` icon. + TRASH_X (IconName): The :code:`trash-x` icon. + TRASH_X_FILLED (IconName): The :code:`trash-x-filled` icon. + TREADMILL (IconName): The :code:`treadmill` icon. + TREE (IconName): The :code:`tree` icon. + TREES (IconName): The :code:`trees` icon. + TREKKING (IconName): The :code:`trekking` icon. + TRENDING_DOWN (IconName): The :code:`trending-down` icon. + TRENDING_DOWN_2 (IconName): The :code:`trending-down-2` icon. + TRENDING_DOWN_3 (IconName): The :code:`trending-down-3` icon. + TRENDING_UP (IconName): The :code:`trending-up` icon. + TRENDING_UP_2 (IconName): The :code:`trending-up-2` icon. + TRENDING_UP_3 (IconName): The :code:`trending-up-3` icon. + TRIANGLE (IconName): The :code:`triangle` icon. + TRIANGLE_FILLED (IconName): The :code:`triangle-filled` icon. + TRIANGLE_INVERTED (IconName): The :code:`triangle-inverted` icon. + TRIANGLE_INVERTED_FILLED (IconName): The :code:`triangle-inverted-filled` icon. + TRIANGLE_OFF (IconName): The :code:`triangle-off` icon. + TRIANGLE_SQUARE_CIRCLE (IconName): The :code:`triangle-square-circle` icon. + TRIANGLES (IconName): The :code:`triangles` icon. + TRIDENT (IconName): The :code:`trident` icon. + TROLLEY (IconName): The :code:`trolley` icon. + TROPHY (IconName): The :code:`trophy` icon. + TROPHY_FILLED (IconName): The :code:`trophy-filled` icon. + TROPHY_OFF (IconName): The :code:`trophy-off` icon. + TROWEL (IconName): The :code:`trowel` icon. + TRUCK (IconName): The :code:`truck` icon. + TRUCK_DELIVERY (IconName): The :code:`truck-delivery` icon. + TRUCK_LOADING (IconName): The :code:`truck-loading` icon. + TRUCK_OFF (IconName): The :code:`truck-off` icon. + TRUCK_RETURN (IconName): The :code:`truck-return` icon. + TXT (IconName): The :code:`txt` icon. + TYPOGRAPHY (IconName): The :code:`typography` icon. + TYPOGRAPHY_OFF (IconName): The :code:`typography-off` icon. + UFO (IconName): The :code:`ufo` icon. + UFO_OFF (IconName): The :code:`ufo-off` icon. + UMBRELLA (IconName): The :code:`umbrella` icon. + UMBRELLA_FILLED (IconName): The :code:`umbrella-filled` icon. + UMBRELLA_OFF (IconName): The :code:`umbrella-off` icon. + UNDERLINE (IconName): The :code:`underline` icon. + UNLINK (IconName): The :code:`unlink` icon. + UPLOAD (IconName): The :code:`upload` icon. + URGENT (IconName): The :code:`urgent` icon. + USB (IconName): The :code:`usb` icon. + USER (IconName): The :code:`user` icon. + USER_BOLT (IconName): The :code:`user-bolt` icon. + USER_CANCEL (IconName): The :code:`user-cancel` icon. + USER_CHECK (IconName): The :code:`user-check` icon. + USER_CIRCLE (IconName): The :code:`user-circle` icon. + USER_CODE (IconName): The :code:`user-code` icon. + USER_COG (IconName): The :code:`user-cog` icon. + USER_DOLLAR (IconName): The :code:`user-dollar` icon. + USER_DOWN (IconName): The :code:`user-down` icon. + USER_EDIT (IconName): The :code:`user-edit` icon. + USER_EXCLAMATION (IconName): The :code:`user-exclamation` icon. + USER_HEART (IconName): The :code:`user-heart` icon. + USER_MINUS (IconName): The :code:`user-minus` icon. + USER_OFF (IconName): The :code:`user-off` icon. + USER_PAUSE (IconName): The :code:`user-pause` icon. + USER_PIN (IconName): The :code:`user-pin` icon. + USER_PLUS (IconName): The :code:`user-plus` icon. + USER_QUESTION (IconName): The :code:`user-question` icon. + USER_SEARCH (IconName): The :code:`user-search` icon. + USER_SHARE (IconName): The :code:`user-share` icon. + USER_SHIELD (IconName): The :code:`user-shield` icon. + USER_STAR (IconName): The :code:`user-star` icon. + USER_UP (IconName): The :code:`user-up` icon. + USER_X (IconName): The :code:`user-x` icon. + USERS (IconName): The :code:`users` icon. + USERS_GROUP (IconName): The :code:`users-group` icon. + USERS_MINUS (IconName): The :code:`users-minus` icon. + USERS_PLUS (IconName): The :code:`users-plus` icon. + UV_INDEX (IconName): The :code:`uv-index` icon. + UX_CIRCLE (IconName): The :code:`ux-circle` icon. + VACCINE (IconName): The :code:`vaccine` icon. + VACCINE_BOTTLE (IconName): The :code:`vaccine-bottle` icon. + VACCINE_BOTTLE_OFF (IconName): The :code:`vaccine-bottle-off` icon. + VACCINE_OFF (IconName): The :code:`vaccine-off` icon. + VACUUM_CLEANER (IconName): The :code:`vacuum-cleaner` icon. + VARIABLE (IconName): The :code:`variable` icon. + VARIABLE_MINUS (IconName): The :code:`variable-minus` icon. + VARIABLE_OFF (IconName): The :code:`variable-off` icon. + VARIABLE_PLUS (IconName): The :code:`variable-plus` icon. + VECTOR (IconName): The :code:`vector` icon. + VECTOR_BEZIER (IconName): The :code:`vector-bezier` icon. + VECTOR_BEZIER_2 (IconName): The :code:`vector-bezier-2` icon. + VECTOR_BEZIER_ARC (IconName): The :code:`vector-bezier-arc` icon. + VECTOR_BEZIER_CIRCLE (IconName): The :code:`vector-bezier-circle` icon. + VECTOR_OFF (IconName): The :code:`vector-off` icon. + VECTOR_SPLINE (IconName): The :code:`vector-spline` icon. + VECTOR_TRIANGLE (IconName): The :code:`vector-triangle` icon. + VECTOR_TRIANGLE_OFF (IconName): The :code:`vector-triangle-off` icon. + VENUS (IconName): The :code:`venus` icon. + VERSIONS (IconName): The :code:`versions` icon. + VERSIONS_FILLED (IconName): The :code:`versions-filled` icon. + VERSIONS_OFF (IconName): The :code:`versions-off` icon. + VIDEO (IconName): The :code:`video` icon. + VIDEO_MINUS (IconName): The :code:`video-minus` icon. + VIDEO_OFF (IconName): The :code:`video-off` icon. + VIDEO_PLUS (IconName): The :code:`video-plus` icon. + VIEW_360 (IconName): The :code:`view-360` icon. + VIEW_360_OFF (IconName): The :code:`view-360-off` icon. + VIEWFINDER (IconName): The :code:`viewfinder` icon. + VIEWFINDER_OFF (IconName): The :code:`viewfinder-off` icon. + VIEWPORT_NARROW (IconName): The :code:`viewport-narrow` icon. + VIEWPORT_WIDE (IconName): The :code:`viewport-wide` icon. + VINYL (IconName): The :code:`vinyl` icon. + VIP (IconName): The :code:`vip` icon. + VIP_OFF (IconName): The :code:`vip-off` icon. + VIRUS (IconName): The :code:`virus` icon. + VIRUS_OFF (IconName): The :code:`virus-off` icon. + VIRUS_SEARCH (IconName): The :code:`virus-search` icon. + VOCABULARY (IconName): The :code:`vocabulary` icon. + VOCABULARY_OFF (IconName): The :code:`vocabulary-off` icon. + VOLCANO (IconName): The :code:`volcano` icon. + VOLUME (IconName): The :code:`volume` icon. + VOLUME_2 (IconName): The :code:`volume-2` icon. + VOLUME_3 (IconName): The :code:`volume-3` icon. + VOLUME_OFF (IconName): The :code:`volume-off` icon. + WALK (IconName): The :code:`walk` icon. + WALL (IconName): The :code:`wall` icon. + WALL_OFF (IconName): The :code:`wall-off` icon. + WALLET (IconName): The :code:`wallet` icon. + WALLET_OFF (IconName): The :code:`wallet-off` icon. + WALLPAPER (IconName): The :code:`wallpaper` icon. + WALLPAPER_OFF (IconName): The :code:`wallpaper-off` icon. + WAND (IconName): The :code:`wand` icon. + WAND_OFF (IconName): The :code:`wand-off` icon. + WASH (IconName): The :code:`wash` icon. + WASH_DRY (IconName): The :code:`wash-dry` icon. + WASH_DRY_1 (IconName): The :code:`wash-dry-1` icon. + WASH_DRY_2 (IconName): The :code:`wash-dry-2` icon. + WASH_DRY_3 (IconName): The :code:`wash-dry-3` icon. + WASH_DRY_A (IconName): The :code:`wash-dry-a` icon. + WASH_DRY_DIP (IconName): The :code:`wash-dry-dip` icon. + WASH_DRY_F (IconName): The :code:`wash-dry-f` icon. + WASH_DRY_FLAT (IconName): The :code:`wash-dry-flat` icon. + WASH_DRY_HANG (IconName): The :code:`wash-dry-hang` icon. + WASH_DRY_OFF (IconName): The :code:`wash-dry-off` icon. + WASH_DRY_P (IconName): The :code:`wash-dry-p` icon. + WASH_DRY_SHADE (IconName): The :code:`wash-dry-shade` icon. + WASH_DRY_W (IconName): The :code:`wash-dry-w` icon. + WASH_DRYCLEAN (IconName): The :code:`wash-dryclean` icon. + WASH_DRYCLEAN_OFF (IconName): The :code:`wash-dryclean-off` icon. + WASH_ECO (IconName): The :code:`wash-eco` icon. + WASH_GENTLE (IconName): The :code:`wash-gentle` icon. + WASH_HAND (IconName): The :code:`wash-hand` icon. + WASH_MACHINE (IconName): The :code:`wash-machine` icon. + WASH_OFF (IconName): The :code:`wash-off` icon. + WASH_PRESS (IconName): The :code:`wash-press` icon. + WASH_TEMPERATURE_1 (IconName): The :code:`wash-temperature-1` icon. + WASH_TEMPERATURE_2 (IconName): The :code:`wash-temperature-2` icon. + WASH_TEMPERATURE_3 (IconName): The :code:`wash-temperature-3` icon. + WASH_TEMPERATURE_4 (IconName): The :code:`wash-temperature-4` icon. + WASH_TEMPERATURE_5 (IconName): The :code:`wash-temperature-5` icon. + WASH_TEMPERATURE_6 (IconName): The :code:`wash-temperature-6` icon. + WASH_TUMBLE_DRY (IconName): The :code:`wash-tumble-dry` icon. + WASH_TUMBLE_OFF (IconName): The :code:`wash-tumble-off` icon. + WATERPOLO (IconName): The :code:`waterpolo` icon. + WAVE_SAW_TOOL (IconName): The :code:`wave-saw-tool` icon. + WAVE_SINE (IconName): The :code:`wave-sine` icon. + WAVE_SQUARE (IconName): The :code:`wave-square` icon. + WEBHOOK (IconName): The :code:`webhook` icon. + WEBHOOK_OFF (IconName): The :code:`webhook-off` icon. + WEIGHT (IconName): The :code:`weight` icon. + WHEELCHAIR (IconName): The :code:`wheelchair` icon. + WHEELCHAIR_OFF (IconName): The :code:`wheelchair-off` icon. + WHIRL (IconName): The :code:`whirl` icon. + WIFI (IconName): The :code:`wifi` icon. + WIFI_0 (IconName): The :code:`wifi-0` icon. + WIFI_1 (IconName): The :code:`wifi-1` icon. + WIFI_2 (IconName): The :code:`wifi-2` icon. + WIFI_OFF (IconName): The :code:`wifi-off` icon. + WIND (IconName): The :code:`wind` icon. + WIND_OFF (IconName): The :code:`wind-off` icon. + WINDMILL (IconName): The :code:`windmill` icon. + WINDMILL_FILLED (IconName): The :code:`windmill-filled` icon. + WINDMILL_OFF (IconName): The :code:`windmill-off` icon. + WINDOW (IconName): The :code:`window` icon. + WINDOW_MAXIMIZE (IconName): The :code:`window-maximize` icon. + WINDOW_MINIMIZE (IconName): The :code:`window-minimize` icon. + WINDOW_OFF (IconName): The :code:`window-off` icon. + WINDSOCK (IconName): The :code:`windsock` icon. + WIPER (IconName): The :code:`wiper` icon. + WIPER_WASH (IconName): The :code:`wiper-wash` icon. + WOMAN (IconName): The :code:`woman` icon. + WOOD (IconName): The :code:`wood` icon. + WORLD (IconName): The :code:`world` icon. + WORLD_BOLT (IconName): The :code:`world-bolt` icon. + WORLD_CANCEL (IconName): The :code:`world-cancel` icon. + WORLD_CHECK (IconName): The :code:`world-check` icon. + WORLD_CODE (IconName): The :code:`world-code` icon. + WORLD_COG (IconName): The :code:`world-cog` icon. + WORLD_DOLLAR (IconName): The :code:`world-dollar` icon. + WORLD_DOWN (IconName): The :code:`world-down` icon. + WORLD_DOWNLOAD (IconName): The :code:`world-download` icon. + WORLD_EXCLAMATION (IconName): The :code:`world-exclamation` icon. + WORLD_HEART (IconName): The :code:`world-heart` icon. + WORLD_LATITUDE (IconName): The :code:`world-latitude` icon. + WORLD_LONGITUDE (IconName): The :code:`world-longitude` icon. + WORLD_MINUS (IconName): The :code:`world-minus` icon. + WORLD_OFF (IconName): The :code:`world-off` icon. + WORLD_PAUSE (IconName): The :code:`world-pause` icon. + WORLD_PIN (IconName): The :code:`world-pin` icon. + WORLD_PLUS (IconName): The :code:`world-plus` icon. + WORLD_QUESTION (IconName): The :code:`world-question` icon. + WORLD_SEARCH (IconName): The :code:`world-search` icon. + WORLD_SHARE (IconName): The :code:`world-share` icon. + WORLD_STAR (IconName): The :code:`world-star` icon. + WORLD_UP (IconName): The :code:`world-up` icon. + WORLD_UPLOAD (IconName): The :code:`world-upload` icon. + WORLD_WWW (IconName): The :code:`world-www` icon. + WORLD_X (IconName): The :code:`world-x` icon. + WRECKING_BALL (IconName): The :code:`wrecking-ball` icon. + WRITING (IconName): The :code:`writing` icon. + WRITING_OFF (IconName): The :code:`writing-off` icon. + WRITING_SIGN (IconName): The :code:`writing-sign` icon. + WRITING_SIGN_OFF (IconName): The :code:`writing-sign-off` icon. + X (IconName): The :code:`x` icon. + XBOX_A (IconName): The :code:`xbox-a` icon. + XBOX_B (IconName): The :code:`xbox-b` icon. + XBOX_X (IconName): The :code:`xbox-x` icon. + XBOX_Y (IconName): The :code:`xbox-y` icon. + XD (IconName): The :code:`xd` icon. + YIN_YANG (IconName): The :code:`yin-yang` icon. + YIN_YANG_FILLED (IconName): The :code:`yin-yang-filled` icon. + YOGA (IconName): The :code:`yoga` icon. + ZEPPELIN (IconName): The :code:`zeppelin` icon. + ZEPPELIN_OFF (IconName): The :code:`zeppelin-off` icon. + ZIP (IconName): The :code:`zip` icon. + ZODIAC_AQUARIUS (IconName): The :code:`zodiac-aquarius` icon. + ZODIAC_ARIES (IconName): The :code:`zodiac-aries` icon. + ZODIAC_CANCER (IconName): The :code:`zodiac-cancer` icon. + ZODIAC_CAPRICORN (IconName): The :code:`zodiac-capricorn` icon. + ZODIAC_GEMINI (IconName): The :code:`zodiac-gemini` icon. + ZODIAC_LEO (IconName): The :code:`zodiac-leo` icon. + ZODIAC_LIBRA (IconName): The :code:`zodiac-libra` icon. + ZODIAC_PISCES (IconName): The :code:`zodiac-pisces` icon. + ZODIAC_SAGITTARIUS (IconName): The :code:`zodiac-sagittarius` icon. + ZODIAC_SCORPIO (IconName): The :code:`zodiac-scorpio` icon. + ZODIAC_TAURUS (IconName): The :code:`zodiac-taurus` icon. + ZODIAC_VIRGO (IconName): The :code:`zodiac-virgo` icon. + ZOOM_CANCEL (IconName): The :code:`zoom-cancel` icon. + ZOOM_CHECK (IconName): The :code:`zoom-check` icon. + ZOOM_CHECK_FILLED (IconName): The :code:`zoom-check-filled` icon. + ZOOM_CODE (IconName): The :code:`zoom-code` icon. + ZOOM_EXCLAMATION (IconName): The :code:`zoom-exclamation` icon. + ZOOM_FILLED (IconName): The :code:`zoom-filled` icon. + ZOOM_IN (IconName): The :code:`zoom-in` icon. + ZOOM_IN_AREA (IconName): The :code:`zoom-in-area` icon. + ZOOM_IN_AREA_FILLED (IconName): The :code:`zoom-in-area-filled` icon. + ZOOM_IN_FILLED (IconName): The :code:`zoom-in-filled` icon. + ZOOM_MONEY (IconName): The :code:`zoom-money` icon. + ZOOM_OUT (IconName): The :code:`zoom-out` icon. + ZOOM_OUT_AREA (IconName): The :code:`zoom-out-area` icon. + ZOOM_OUT_FILLED (IconName): The :code:`zoom-out-filled` icon. + ZOOM_PAN (IconName): The :code:`zoom-pan` icon. + ZOOM_QUESTION (IconName): The :code:`zoom-question` icon. + ZOOM_REPLACE (IconName): The :code:`zoom-replace` icon. + ZOOM_RESET (IconName): The :code:`zoom-reset` icon. + ZZZ (IconName): The :code:`zzz` icon. + ZZZ_OFF (IconName): The :code:`zzz-off` icon. + """
+ +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/_scene_api/index.html b/versions/0.2.2/_modules/viser/_scene_api/index.html new file mode 100644 index 000000000..5018c016c --- /dev/null +++ b/versions/0.2.2/_modules/viser/_scene_api/index.html @@ -0,0 +1,1724 @@ + + + + + + + + + + + + viser._scene_api - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser._scene_api

+from __future__ import annotations
+
+import base64
+import io
+import time
+import warnings
+from concurrent.futures import ThreadPoolExecutor
+from typing import TYPE_CHECKING, Callable, Tuple, TypeVar, Union, cast, get_args
+
+import imageio.v3 as iio
+import numpy as onp
+import numpy.typing as onpt
+from typing_extensions import Literal, ParamSpec, TypeAlias, assert_never
+
+from . import _messages
+from . import transforms as tf
+from ._scene_handles import (
+    BatchedAxesHandle,
+    CameraFrustumHandle,
+    FrameHandle,
+    GlbHandle,
+    Gui3dContainerHandle,
+    ImageHandle,
+    LabelHandle,
+    MeshHandle,
+    PointCloudHandle,
+    SceneNodeHandle,
+    SceneNodePointerEvent,
+    ScenePointerEvent,
+    TransformControlsHandle,
+    _SceneNodeHandleState,
+    _TransformControlsState,
+)
+
+if TYPE_CHECKING:
+    import trimesh
+
+    from ._viser import ClientHandle, ViserServer
+    from .infra import ClientId
+
+
+P = ParamSpec("P")
+
+
+def _colors_to_uint8(colors: onp.ndarray) -> onpt.NDArray[onp.uint8]:
+    """Convert intensity values to uint8. We assume the range [0,1] for floats, and
+    [0,255] for integers. Accepts any shape."""
+    if colors.dtype != onp.uint8:
+        if onp.issubdtype(colors.dtype, onp.floating):
+            colors = onp.clip(colors * 255.0, 0, 255).astype(onp.uint8)
+        if onp.issubdtype(colors.dtype, onp.integer):
+            colors = onp.clip(colors, 0, 255).astype(onp.uint8)
+    return colors
+
+
+RgbTupleOrArray: TypeAlias = Union[
+    Tuple[int, int, int], Tuple[float, float, float], onp.ndarray
+]
+
+
+def _encode_rgb(rgb: RgbTupleOrArray) -> int:
+    if isinstance(rgb, onp.ndarray):
+        assert rgb.shape == (3,)
+    rgb_fixed = tuple(
+        value if onp.issubdtype(type(value), onp.integer) else int(value * 255)
+        for value in rgb
+    )
+    assert len(rgb_fixed) == 3
+    return int(rgb_fixed[0] * (256**2) + rgb_fixed[1] * 256 + rgb_fixed[2])
+
+
+def _encode_image_base64(
+    image: onp.ndarray,
+    format: Literal["png", "jpeg"],
+    jpeg_quality: int | None = None,
+) -> tuple[Literal["image/png", "image/jpeg"], str]:
+    media_type: Literal["image/png", "image/jpeg"]
+    image = _colors_to_uint8(image)
+    with io.BytesIO() as data_buffer:
+        if format == "png":
+            media_type = "image/png"
+            iio.imwrite(data_buffer, image, extension=".png")
+        elif format == "jpeg":
+            media_type = "image/jpeg"
+            iio.imwrite(
+                data_buffer,
+                image[..., :3],  # Strip alpha.
+                extension=".jpeg",
+                quality=75 if jpeg_quality is None else jpeg_quality,
+            )
+        else:
+            assert_never(format)
+
+        base64_data = base64.b64encode(data_buffer.getvalue()).decode("ascii")
+
+    return media_type, base64_data
+
+
+TVector = TypeVar("TVector", bound=tuple)
+
+
+def cast_vector(vector: TVector | onp.ndarray, length: int) -> TVector:
+    if not isinstance(vector, tuple):
+        assert cast(onp.ndarray, vector).shape == (
+            length,
+        ), f"Expected vector of shape {(length,)}, but got {vector.shape} instead"
+    return cast(TVector, tuple(map(float, vector)))
+
+
+
+[docs] +class SceneApi: + """Interface for adding 3D primitives to the scene. + + Used by both our global server object, for sharing the same GUI elements + with all clients, and by invidividual client handles.""" + + def __init__( + self, + owner: ViserServer | ClientHandle, # Who do I belong to? + thread_executor: ThreadPoolExecutor, + ) -> None: + from ._viser import ViserServer + + self._owner = owner + """Entity that owns this API.""" + + self._websock_interface = ( + owner._websock_server + if isinstance(owner, ViserServer) + else owner._websock_connection + ) + """Interface for sending and listening to messages.""" + + self.world_axes: FrameHandle = FrameHandle( + _SceneNodeHandleState( + "/WorldAxes", + self, + wxyz=onp.array([1.0, 0.0, 0.0, 0.0]), + position=onp.zeros(3), + ) + ) + """Handle for the world axes, which are created by default.""" + + # Hide world axes on initialization. + if isinstance(owner, ViserServer): + self.world_axes.visible = False + + self._handle_from_transform_controls_name: dict[ + str, TransformControlsHandle + ] = {} + self._handle_from_node_name: dict[str, SceneNodeHandle] = {} + + self._scene_pointer_cb: Callable[[ScenePointerEvent], None] | None = None + self._scene_pointer_done_cb: Callable[[], None] = lambda: None + self._scene_pointer_event_type: _messages.ScenePointerEventType | None = None + + self._websock_interface.register_handler( + _messages.TransformControlsUpdateMessage, + self._handle_transform_controls_updates, + ) + self._websock_interface.register_handler( + _messages.SceneNodeClickMessage, + self._handle_node_click_updates, + ) + self._websock_interface.register_handler( + _messages.ScenePointerMessage, + self._handle_scene_pointer_updates, + ) + + self._thread_executor = thread_executor + +
+[docs] + def set_up_direction( + self, + direction: Literal["+x", "+y", "+z", "-x", "-y", "-z"] + | tuple[float, float, float] + | onp.ndarray, + ) -> None: + """Set the global up direction of the scene. By default we follow +Z-up + (similar to Blender, 3DS Max, ROS, etc), the most common alternative is + +Y (OpenGL, Maya, etc). + + Args: + direction: New up direction. Can either be a string (one of +x, +y, + +z, -x, -y, -z) or a length-3 direction vector. + """ + if isinstance(direction, str): + direction = { + "+x": (1, 0, 0), + "+y": (0, 1, 0), + "+z": (0, 0, 1), + "-x": (-1, 0, 0), + "-y": (0, -1, 0), + "-z": (0, 0, -1), + }[direction] + assert not isinstance(direction, str) + + default_three_up = onp.array([0.0, 1.0, 0.0]) + direction = onp.asarray(direction) + + def rotate_between(before: onp.ndarray, after: onp.ndarray) -> tf.SO3: + assert before.shape == after.shape == (3,) + before = before / onp.linalg.norm(before) + after = after / onp.linalg.norm(after) + + angle = onp.arccos(onp.clip(onp.dot(before, after), -1, 1)) + axis = onp.cross(before, after) + if onp.allclose(axis, onp.zeros(3), rtol=1e-3, atol=1e-5): + unit_vector = onp.arange(3) == onp.argmin(onp.abs(before)) + axis = onp.cross(before, unit_vector) + axis = axis / onp.linalg.norm(axis) + return tf.SO3.exp(angle * axis) + + R_threeworld_world = rotate_between(direction, default_three_up) + + # Rotate the world frame such that: + # If we set +Y to up, +X and +Z should face the camera. + # If we set +Z to up, +X and +Y should face the camera. + # In App.tsx, the camera is initialized at [-3, 3, -3] in the threejs + # coordinate frame. + desired_fwd = onp.array([-1.0, 0.0, -1.0]) / onp.sqrt(2.0) + current_fwd = R_threeworld_world @ (onp.ones(3) / onp.sqrt(3.0)) + current_fwd = current_fwd * onp.array([1.0, 0.0, 1.0]) + current_fwd = current_fwd / onp.linalg.norm(current_fwd) + R_threeworld_world = ( + tf.SO3.from_y_radians( # Rotate around the null space / up direction. + onp.arctan2( + onp.cross(current_fwd, desired_fwd)[1], + onp.dot(current_fwd, desired_fwd), + ), + ) +
+[docs] + @ R_threeworld_world + ) + + if not onp.any(onp.isnan(R_threeworld_world.wxyz)): + # Set the orientation of the root node. + self._websock_interface.queue_message( + _messages.SetOrientationMessage( + "", cast_vector(R_threeworld_world.wxyz, 4) + ) + )
+ + + def set_global_visibility(self, visible: bool) -> None: + """Set visibility for all scene nodes. If set to False, all scene nodes + will be hidden. + + This can be useful when we've called + :meth:`SceneApi.set_background_image()`, and want to hide everything + except for the background. + + Args: + visible: Whether or not all scene nodes should be visible. + """ + self._websock_interface.queue_message( + _messages.SetSceneNodeVisibilityMessage("", visible) + )
+ + +
+[docs] + def add_glb( + self, + name: str, + glb_data: bytes, + scale=1.0, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> GlbHandle: + """Add a general 3D asset via binary glTF (GLB). + + For glTF files, it's often simpler to use `trimesh.load()` with + `.add_mesh_trimesh()`. This will call `.add_glb()` under the hood. + + For glTF features not supported by trimesh, glTF to GLB conversion can + also be done programatically with libraries like `pygltflib`. + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + glb_data: A binary payload. + scale: A scale for resizing the GLB asset. + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation to parent frame from local frame (t_pl). + visible: Whether or not this scene node is initially visible. + + Returns: + Handle for manipulating scene node. + """ + self._websock_interface.queue_message( + _messages.GlbMessage(name, glb_data, scale) + ) + return GlbHandle._make(self, name, wxyz, position, visible)
+ + +
+[docs] + def add_spline_catmull_rom( + self, + name: str, + positions: tuple[tuple[float, float, float], ...] | onp.ndarray, + curve_type: Literal["centripetal", "chordal", "catmullrom"] = "centripetal", + tension: float = 0.5, + closed: bool = False, + line_width: float = 1, + color: RgbTupleOrArray = (20, 20, 20), + segments: int | None = None, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> SceneNodeHandle: + """Add a spline to the scene using Catmull-Rom interpolation. + + This method creates a spline based on a set of positions and interpolates + them using the Catmull-Rom algorithm. This can be used to create smooth curves. + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + positions: A tuple of 3D positions (x, y, z) defining the spline's path. + curve_type: Type of the curve ('centripetal', 'chordal', 'catmullrom'). + tension: Tension of the curve. Affects the tightness of the curve. + closed: Boolean indicating if the spline is closed (forms a loop). + line_width: Width of the spline line. + color: Color of the spline as an RGB tuple. + segments: Number of segments to divide the spline into. + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation to parent frame from local frame (t_pl). + visible: Whether or not this scene node is initially visible. + + Returns: + Handle for manipulating scene node. + """ + if isinstance(positions, onp.ndarray): + assert len(positions.shape) == 2 and positions.shape[1] == 3 + positions = tuple(map(tuple, positions)) # type: ignore + assert len(positions[0]) == 3 + assert isinstance(positions, tuple) + self._websock_interface.queue_message( + _messages.CatmullRomSplineMessage( + name, + positions, + curve_type, + tension, + closed, + line_width, + _encode_rgb(color), + segments=segments, + ) + ) + return SceneNodeHandle._make(self, name, wxyz, position, visible)
+ + +
+[docs] + def add_spline_cubic_bezier( + self, + name: str, + positions: tuple[tuple[float, float, float], ...] | onp.ndarray, + control_points: tuple[tuple[float, float, float], ...] | onp.ndarray, + line_width: float = 1, + color: RgbTupleOrArray = (20, 20, 20), + segments: int | None = None, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> SceneNodeHandle: + """Add a spline to the scene using Cubic Bezier interpolation. + + This method allows for the creation of a cubic Bezier spline based on given + positions and control points. It is useful for creating complex, smooth, + curving shapes. + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + positions: A tuple of 3D positions (x, y, z) defining the spline's key points. + control_points: A tuple of control points for Bezier curve shaping. + line_width: Width of the spline line. + color: Color of the spline as an RGB tuple. + segments: Number of segments to divide the spline into. + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation to parent frame from local frame (t_pl). + visible: Whether or not this scene node is initially visible. + + Returns: + Handle for manipulating scene node. + """ + + if isinstance(positions, onp.ndarray): + assert len(positions.shape) == 2 and positions.shape[1] == 3 + positions = tuple(map(tuple, positions)) # type: ignore + if isinstance(control_points, onp.ndarray): + assert len(control_points.shape) == 2 and control_points.shape[1] == 3 + control_points = tuple(map(tuple, control_points)) # type: ignore + + assert isinstance(positions, tuple) + assert isinstance(control_points, tuple) + assert len(control_points) == (2 * len(positions) - 2) + self._websock_interface.queue_message( + _messages.CubicBezierSplineMessage( + name, + positions, + control_points, + line_width, + _encode_rgb(color), + segments=segments, + ) + ) + return SceneNodeHandle._make(self, name, wxyz, position, visible)
+ + +
+[docs] + def add_camera_frustum( + self, + name: str, + fov: float, + aspect: float, + scale: float = 0.3, + color: RgbTupleOrArray = (20, 20, 20), + image: onp.ndarray | None = None, + format: Literal["png", "jpeg"] = "jpeg", + jpeg_quality: int | None = None, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> CameraFrustumHandle: + """Add a camera frustum to the scene for visualization. + + This method adds a frustum representation, typically used to visualize the + field of view of a camera. It's helpful for understanding the perspective + and coverage of a camera in the 3D space. + + Like all cameras in the viser Python API, frustums follow the OpenCV [+Z forward, + +X right, +Y down] convention. fov is vertical in radians; aspect is width over height + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + fov: Field of view of the camera (in radians). + aspect: Aspect ratio of the camera (width over height). + scale: Scale factor for the size of the frustum. + color: Color of the frustum as an RGB tuple. + image: Optional image to be displayed on the frustum. + format: Format of the provided image ('png' or 'jpeg'). + jpeg_quality: Quality of the jpeg image (if jpeg format is used). + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation to parent frame from local frame (t_pl). + visible: Whether or not this scene node is initially visible. + + Returns: + Handle for manipulating scene node. + """ + + if image is not None: + media_type, base64_data = _encode_image_base64( + image, format, jpeg_quality=jpeg_quality + ) + else: + media_type = None + base64_data = None + + self._websock_interface.queue_message( + _messages.CameraFrustumMessage( + name=name, + fov=fov, + aspect=aspect, + scale=scale, + # (255, 255, 255) => 0xffffff, etc + color=_encode_rgb(color), + image_media_type=media_type, + image_base64_data=base64_data, + ) + ) + return CameraFrustumHandle._make(self, name, wxyz, position, visible)
+ + +
+[docs] + def add_frame( + self, + name: str, + show_axes: bool = True, + axes_length: float = 0.5, + axes_radius: float = 0.025, + origin_radius: float | None = None, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> FrameHandle: + """Add a coordinate frame to the scene. + + This method is used for adding a visual representation of a coordinate + frame, which can help in understanding the orientation and position of + objects in 3D space. + + For cases where we want to visualize many coordinate frames, like + trajectories containing thousands or tens of thousands of frames, + batching and calling `add_batched_axes()` may be a better choice than calling + `add_frame()` in a loop. + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + show_axes: Boolean to indicate whether to show the frame as a set of axes + origin sphere. + axes_length: Length of each axis. + axes_radius: Radius of each axis. + origin_radius: Radius of the origin sphere. If not set, defaults to `2 * axes_radius`. + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation to parent frame from local frame (t_pl). + visible: Whether or not this scene node is initially visible. + + Returns: + Handle for manipulating scene node. + """ + if origin_radius is None: + origin_radius = axes_radius * 2 + self._websock_interface.queue_message( + _messages.FrameMessage( + name=name, + show_axes=show_axes, + axes_length=axes_length, + axes_radius=axes_radius, + origin_radius=origin_radius, + ) + ) + return FrameHandle._make(self, name, wxyz, position, visible)
+ + +
+[docs] + def add_batched_axes( + self, + name: str, + batched_wxyzs: tuple[tuple[float, float, float, float], ...] | onp.ndarray, + batched_positions: tuple[tuple[float, float, float], ...] | onp.ndarray, + axes_length: float = 0.5, + axes_radius: float = 0.025, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> BatchedAxesHandle: + """Visualize batched sets of coordinate frame axes. + + The functionality of `add_batched_axes()` overlaps significantly with + `add_frame()` when `show_axes=True`. The primary difference is that + `add_batched_axes()` supports multiple axes via the `wxyzs_batched` + (shape Nx4) and `positions_batched` (shape Nx3) arguments. + + Axes that are batched and rendered via a single call to + `add_batched_axes()` are instanced on the client; this will be much + faster to render than `add_frame()` called in a loop. + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + batched_wxyzs: Float array of shape (N,4). + batched_positions: Float array of shape (N,3). + axes_length: Length of each axis. + axes_radius: Radius of each axis. + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + This will be applied to all axes. + position: Translation to parent frame from local frame (t_pl). + This will be applied to all axes. + visible: Whether or not this scene node is initially visible. + + Returns: + Handle for manipulating scene node. + """ + batched_wxyzs = onp.asarray(batched_wxyzs) + batched_positions = onp.asarray(batched_positions) + + num_axes = batched_wxyzs.shape[0] + assert batched_wxyzs.shape == (num_axes, 4) + assert batched_positions.shape == (num_axes, 3) + self._websock_interface.queue_message( + _messages.BatchedAxesMessage( + name=name, + wxyzs_batched=batched_wxyzs.astype(onp.float32), + positions_batched=batched_positions.astype(onp.float32), + axes_length=axes_length, + axes_radius=axes_radius, + ) + ) + return BatchedAxesHandle._make(self, name, wxyz, position, visible)
+ + +
+[docs] + def add_grid( + self, + name: str, + width: float = 10.0, + height: float = 10.0, + width_segments: int = 10, + height_segments: int = 10, + plane: Literal["xz", "xy", "yx", "yz", "zx", "zy"] = "xy", + cell_color: RgbTupleOrArray = (200, 200, 200), + cell_thickness: float = 1.0, + cell_size: float = 0.5, + section_color: RgbTupleOrArray = (140, 140, 140), + section_thickness: float = 1.0, + section_size: float = 1.0, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> SceneNodeHandle: + """Add a 2D grid to the scene. + + This can be useful as a size, orientation, or ground plane reference. + + Args: + name: Name of the grid. + width: Width of the grid. + height: Height of the grid. + width_segments: Number of segments along the width. + height_segments: Number of segments along the height. + plane: The plane in which the grid is oriented (e.g., 'xy', 'yz'). + cell_color: Color of the grid cells as an RGB tuple. + cell_thickness: Thickness of the grid lines. + cell_size: Size of each cell in the grid. + section_color: Color of the grid sections as an RGB tuple. + section_thickness: Thickness of the section lines. + section_size: Size of each section in the grid. + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation to parent frame from local frame (t_pl). + visible: Whether or not this scene node is initially visible. + + Returns: + Handle for manipulating scene node. + """ + self._websock_interface.queue_message( + _messages.GridMessage( + name=name, + width=width, + height=height, + width_segments=width_segments, + height_segments=height_segments, + plane=plane, + cell_color=_encode_rgb(cell_color), + cell_thickness=cell_thickness, + cell_size=cell_size, + section_color=_encode_rgb(section_color), + section_thickness=section_thickness, + section_size=section_size, + ) + ) + return SceneNodeHandle._make(self, name, wxyz, position, visible)
+ + +
+[docs] + def add_label( + self, + name: str, + text: str, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> LabelHandle: + """Add a 2D label to the scene. + + This method creates a text label in the 3D scene, which can be used to annotate + or provide information about specific points or objects. + + Args: + name: Name of the label. + text: Text content of the label. + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation to parent frame from local frame (t_pl). + visible: Whether or not this scene node is initially visible. + + Returns: + Handle for manipulating scene node. + """ + self._websock_interface.queue_message(_messages.LabelMessage(name, text)) + return LabelHandle._make(self, name, wxyz, position, visible=visible)
+ + +
+[docs] + def add_point_cloud( + self, + name: str, + points: onp.ndarray, + colors: onp.ndarray | tuple[float, float, float], + point_size: float = 0.1, + point_shape: Literal[ + "square", "diamond", "circle", "rounded", "sparkle" + ] = "square", + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> PointCloudHandle: + """Add a point cloud to the scene. + + Args: + name: Name of scene node. Determines location in kinematic tree. + points: Location of points. Should have shape (N, 3). + colors: Colors of points. Should have shape (N, 3) or (3,). + point_size: Size of each point. + point_shape: Shape to draw each point. + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation to parent frame from local frame (t_pl). + visible: Whether or not this scene node is initially visible. + + Returns: + Handle for manipulating scene node. + """ + colors_cast = _colors_to_uint8(onp.asarray(colors)) + assert ( + len(points.shape) == 2 and points.shape[-1] == 3 + ), "Shape of points should be (N, 3)." + assert colors_cast.shape in { + points.shape, + (3,), + }, "Shape of colors should be (N, 3) or (3,)." + + if colors_cast.shape == (3,): + colors_cast = onp.tile(colors_cast[None, :], reps=(points.shape[0], 1)) + + self._websock_interface.queue_message( + _messages.PointCloudMessage( + name=name, + points=points.astype(onp.float32), + colors=colors_cast, + point_size=point_size, + point_ball_norm={ + "square": float("inf"), + "diamond": 1.0, + "circle": 2.0, + "rounded": 3.0, + "sparkle": 0.6, + }[point_shape], + ) + ) + return PointCloudHandle._make(self, name, wxyz, position, visible)
+ + +
+[docs] + def add_mesh_simple( + self, + name: str, + vertices: onp.ndarray, + faces: onp.ndarray, + color: RgbTupleOrArray = (90, 200, 255), + wireframe: bool = False, + opacity: float | None = None, + material: Literal["standard", "toon3", "toon5"] = "standard", + flat_shading: bool = False, + side: Literal["front", "back", "double"] = "front", + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> MeshHandle: + """Add a mesh to the scene. + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + vertices: A numpy array of vertex positions. Should have shape (V, 3). + faces: A numpy array of faces, where each face is represented by indices of + vertices. Should have shape (F,) + color: Color of the mesh as an RGB tuple. + wireframe: Boolean indicating if the mesh should be rendered as a wireframe. + opacity: Opacity of the mesh. None means opaque. + material: Material type of the mesh ('standard', 'toon3', 'toon5'). + This argument is ignored when wireframe=True. + flat_shading: Whether to do flat shading. This argument is ignored + when wireframe=True. + side: Side of the surface to render ('front', 'back', 'double'). + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation from parent frame to local frame (t_pl). + visible: Whether or not this mesh is initially visible. + + Returns: + Handle for manipulating scene node. + """ + if wireframe and material != "standard": + warnings.warn( + f"Invalid combination of {wireframe=} and {material=}. Material argument will be ignored.", + stacklevel=2, + ) + if wireframe and flat_shading: + warnings.warn( + f"Invalid combination of {wireframe=} and {flat_shading=}. Flat shading argument will be ignored.", + stacklevel=2, + ) + + self._websock_interface.queue_message( + _messages.MeshMessage( + name, + vertices.astype(onp.float32), + faces.astype(onp.uint32), + # (255, 255, 255) => 0xffffff, etc + color=_encode_rgb(color), + vertex_colors=None, + wireframe=wireframe, + opacity=opacity, + flat_shading=flat_shading, + side=side, + material=material, + ) + ) + return MeshHandle._make(self, name, wxyz, position, visible)
+ + +
+[docs] + def add_mesh_trimesh( + self, + name: str, + mesh: trimesh.Trimesh, + scale: float = 1.0, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> GlbHandle: + """Add a trimesh mesh to the scene. Internally calls `self.add_glb()`. + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + mesh: A trimesh mesh object. + scale: A scale for resizing the mesh. + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation to parent frame from local frame (t_pl). + visible: Whether or not this scene node is initially visible. + + Returns: + Handle for manipulating scene node. + """ + + with io.BytesIO() as data_buffer: + mesh.export(data_buffer, file_type="glb") + glb_data = data_buffer.getvalue() + return self.add_glb( + name, + glb_data=glb_data, + scale=scale, + wxyz=wxyz, + position=position, + visible=visible, + )
+ + +
+[docs] + def add_box( + self, + name: str, + color: RgbTupleOrArray, + dimensions: tuple[float, float, float] | onp.ndarray = (1.0, 1.0, 1.0), + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> MeshHandle: + """Add a box to the scene. + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + color: Color of the box as an RGB tuple. + dimensions: Dimensions of the box (x, y, z). + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation from parent frame to local frame (t_pl). + visible: Whether or not this box is initially visible. + + Returns: + Handle for manipulating scene node. + """ + import trimesh.creation + + mesh = trimesh.creation.box(dimensions) + + return self.add_mesh_simple( + name=name, + vertices=mesh.vertices, + faces=mesh.faces, + color=color, + flat_shading=True, + position=position, + wxyz=wxyz, + visible=visible, + )
+ + +
+[docs] + def add_icosphere( + self, + name: str, + radius: float, + color: RgbTupleOrArray, + subdivisions: int = 3, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> MeshHandle: + """Add an icosphere to the scene. + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + radius: Radius of the icosphere. + color: Color of the icosphere as an RGB tuple. + subdivisions: Number of subdivisions to use when creating the icosphere. + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation from parent frame to local frame (t_pl). + visible: Whether or not this icosphere is initially visible. + + Returns: + Handle for manipulating scene node. + """ + import trimesh.creation + + mesh = trimesh.creation.icosphere(subdivisions=subdivisions, radius=radius) + + # We use add_mesh_simple() because it lets us do smooth shading; + # add_mesh_trimesh() currently does not. + return self.add_mesh_simple( + name=name, + vertices=mesh.vertices, + faces=mesh.faces, + color=color, + flat_shading=False, + position=position, + wxyz=wxyz, + visible=visible, + )
+ + +
+[docs] + def set_background_image( + self, + image: onp.ndarray, + format: Literal["png", "jpeg"] = "jpeg", + jpeg_quality: int | None = None, + depth: onp.ndarray | None = None, + ) -> None: + """Set a background image for the scene, optionally with depth compositing. + + Args: + image: The image to set as the background. Should have shape (H, W, 3). + format: Format to transport and display the image using ('png' or 'jpeg'). + jpeg_quality: Quality of the jpeg image (if jpeg format is used). + depth: Optional depth image to use to composite background with scene elements. + """ + media_type, base64_data = _encode_image_base64( + image, format, jpeg_quality=jpeg_quality + ) + + # Encode depth if provided. We use a 3-channel PNG to represent a fixed point + # depth at each pixel. + depth_base64data = None + if depth is not None: + # Convert to fixed-point. + # We'll support from 0 -> (2^24 - 1) / 100_000. + # + # This translates to a range of [0, 167.77215], with a precision of 1e-5. + assert len(depth.shape) == 2 or ( + len(depth.shape) == 3 and depth.shape[2] == 1 + ), "Depth should have shape (H,W) or (H,W,1)." + depth = onp.clip(depth * 100_000, 0, 2**24 - 1).astype(onp.uint32) + assert depth is not None # Appease mypy. + intdepth: onp.ndarray = depth.reshape((*depth.shape[:2], 1)).view(onp.uint8) + assert intdepth.shape == (*depth.shape[:2], 4) + with io.BytesIO() as data_buffer: + iio.imwrite(data_buffer, intdepth[:, :, :3], extension=".png") + depth_base64data = base64.b64encode(data_buffer.getvalue()).decode( + "ascii" + ) + + self._websock_interface.queue_message( + _messages.BackgroundImageMessage( + media_type=media_type, + base64_rgb=base64_data, + base64_depth=depth_base64data, + ) + )
+ + +
+[docs] + def add_image( + self, + name: str, + image: onp.ndarray, + render_width: float, + render_height: float, + format: Literal["png", "jpeg"] = "jpeg", + jpeg_quality: int | None = None, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> ImageHandle: + """Add a 2D image to the scene. + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + image: A numpy array representing the image. + render_width: Width at which the image should be rendered in the scene. + render_height: Height at which the image should be rendered in the scene. + format: Format to transport and display the image using ('png' or 'jpeg'). + jpeg_quality: Quality of the jpeg image (if jpeg format is used). + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation from parent frame to local frame (t_pl). + visible: Whether or not this image is initially visible. + + Returns: + Handle for manipulating scene node. + """ + + media_type, base64_data = _encode_image_base64( + image, format, jpeg_quality=jpeg_quality + ) + self._websock_interface.queue_message( + _messages.ImageMessage( + name=name, + media_type=media_type, + base64_data=base64_data, + render_width=render_width, + render_height=render_height, + ) + ) + return ImageHandle._make(self, name, wxyz, position, visible)
+ + +
+[docs] + def add_transform_controls( + self, + name: str, + scale: float = 1.0, + line_width: float = 2.5, + fixed: bool = False, + auto_transform: bool = True, + active_axes: tuple[bool, bool, bool] = (True, True, True), + disable_axes: bool = False, + disable_sliders: bool = False, + disable_rotations: bool = False, + translation_limits: tuple[ + tuple[float, float], tuple[float, float], tuple[float, float] + ] = ((-1000.0, 1000.0), (-1000.0, 1000.0), (-1000.0, 1000.0)), + rotation_limits: tuple[ + tuple[float, float], tuple[float, float], tuple[float, float] + ] = ((-1000.0, 1000.0), (-1000.0, 1000.0), (-1000.0, 1000.0)), + depth_test: bool = True, + opacity: float = 1.0, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> TransformControlsHandle: + """Add a transform gizmo for interacting with the scene. + + This method adds a transform control (gizmo) to the scene, allowing for interactive + manipulation of objects in terms of their position, rotation, and scale. + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + scale: Scale of the transform controls. + line_width: Width of the lines used in the gizmo. + fixed: Boolean indicating if the gizmo should be fixed in position. + auto_transform: Whether the transform should be applied automatically. + active_axes: tuple of booleans indicating active axes. + disable_axes: Boolean to disable axes interaction. + disable_sliders: Boolean to disable slider interaction. + disable_rotations: Boolean to disable rotation interaction. + translation_limits: Limits for translation. + rotation_limits: Limits for rotation. + depth_test: Boolean indicating if depth testing should be used when rendering. + opacity: Opacity of the gizmo. + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation from parent frame to local frame (t_pl). + visible: Whether or not this gizmo is initially visible. + + Returns: + Handle for manipulating (and reading state of) scene node. + """ + self._websock_interface.queue_message( + _messages.TransformControlsMessage( + name=name, + scale=scale, + line_width=line_width, + fixed=fixed, + auto_transform=auto_transform, + active_axes=active_axes, + disable_axes=disable_axes, + disable_sliders=disable_sliders, + disable_rotations=disable_rotations, + translation_limits=translation_limits, + rotation_limits=rotation_limits, + depth_test=depth_test, + opacity=opacity, + ) + ) + + def sync_cb(client_id: ClientId, state: TransformControlsHandle) -> None: + message_orientation = _messages.SetOrientationMessage( + name=name, + wxyz=tuple(map(float, state._impl.wxyz)), # type: ignore + ) + message_orientation.excluded_self_client = client_id + self._websock_interface.queue_message(message_orientation) + + message_position = _messages.SetPositionMessage( + name=name, + position=tuple(map(float, state._impl.position)), # type: ignore + ) + message_position.excluded_self_client = client_id + self._websock_interface.queue_message(message_position) + + node_handle = SceneNodeHandle._make(self, name, wxyz, position, visible) + state_aux = _TransformControlsState( + last_updated=time.time(), + update_cb=[], + sync_cb=sync_cb, + ) + handle = TransformControlsHandle(node_handle._impl, state_aux) + self._handle_from_transform_controls_name[name] = handle + return handle
+ + +
+[docs] + def reset(self) -> None: + """Reset the scene.""" + self._websock_interface.queue_message(_messages.ResetSceneMessage())
+ + + def _get_client_handle(self, client_id: ClientId) -> ClientHandle: + """Private helper for getting a client handle from its ID.""" + # Avoid circular imports. + from ._viser import ViserServer + + # Implementation-wise, note that MessageApi is never directly instantiated. + # Instead, it serves as a mixin/base class for either ViserServer, which + # maintains a registry of connected clients, or ClientHandle, which should + # only ever be dealing with its own client_id. + if isinstance(self._owner, ViserServer): + # TODO: there's a potential race condition here when the client disconnects. + # This probably applies to multiple other parts of the code, we should + # revisit all of the cases where we index into connected_clients. + return self._owner._connected_clients[client_id] + else: + assert client_id == self._owner.client_id + return self._owner + + def _handle_transform_controls_updates( + self, client_id: ClientId, message: _messages.TransformControlsUpdateMessage + ) -> None: + """Callback for handling transform gizmo messages.""" + handle = self._handle_from_transform_controls_name.get(message.name, None) + if handle is None: + return + + # Update state. + wxyz = onp.array(message.wxyz) + position = onp.array(message.position) + with self._owner.atomic(): + handle._impl.wxyz = wxyz + handle._impl.position = position + handle._impl_aux.last_updated = time.time() + + # Trigger callbacks. + for cb in handle._impl_aux.update_cb: + cb(handle) + if handle._impl_aux.sync_cb is not None: + handle._impl_aux.sync_cb(client_id, handle) + + def _handle_node_click_updates( + self, client_id: ClientId, message: _messages.SceneNodeClickMessage + ) -> None: + """Callback for handling click messages.""" + handle = self._handle_from_node_name.get(message.name, None) + if handle is None or handle._impl.click_cb is None: + return + for cb in handle._impl.click_cb: + event = SceneNodePointerEvent( + client=self._get_client_handle(client_id), + client_id=client_id, + event="click", + target=handle, + ray_origin=message.ray_origin, + ray_direction=message.ray_direction, + ) + cb(event) # type: ignore + + def _handle_scene_pointer_updates( + self, client_id: ClientId, message: _messages.ScenePointerMessage + ): + """Callback for handling click messages.""" + event = ScenePointerEvent( + client=self._get_client_handle(client_id), + client_id=client_id, + event_type=message.event_type, + ray_origin=message.ray_origin, + ray_direction=message.ray_direction, + screen_pos=message.screen_pos, + ) + # Call the callback if it exists, and the after-run callback. + if self._scene_pointer_cb is None: + return + self._scene_pointer_cb(event) + +
+[docs] + def on_pointer_event( + self, event_type: Literal["click", "rect-select"] + ) -> Callable[ + [Callable[[ScenePointerEvent], None]], Callable[[ScenePointerEvent], None] + ]: + """Add a callback for scene pointer events. + + Args: + event_type: event to listen to. + """ + # Ensure the event type is valid. + assert event_type in get_args(_messages.ScenePointerEventType) + + from ._viser import ClientHandle, ViserServer + + def cleanup_previous_event(target: ViserServer | ClientHandle): + # If the server or client does not have a scene pointer callback, return. + if target.scene._scene_pointer_cb is None: + return + + # Remove callback. + target.scene.remove_pointer_callback() + + def decorator( + func: Callable[[ScenePointerEvent], None], + ) -> Callable[[ScenePointerEvent], None]: + # Check if another scene pointer event was previously registered. + # If so, we need to clear the previous event and register the new one. + cleanup_previous_event(self._owner) + + # If called on the server handle, remove all clients' callbacks. + if isinstance(self._owner, ViserServer): + for client in self._owner.get_clients().values(): + cleanup_previous_event(client) + + # If called on the client handle, and server handle has a callback, remove the server's callback. + # (If the server has a callback, none of the clients should have callbacks.) + elif isinstance(self._owner, ClientHandle): + server = self._owner._viser_server + cleanup_previous_event(server) + + self._scene_pointer_cb = func + self._scene_pointer_event_type = event_type + + self._websock_interface.queue_message( + _messages.ScenePointerEnableMessage(enable=True, event_type=event_type) + ) + return func + + return decorator
+ + +
+[docs] + def on_pointer_callback_removed( + self, + func: Callable[[], None], + ) -> Callable[[], None]: + """Add a callback to run automatically when the callback for a scene + pointer event is removed. This will be triggered exactly once, either + manually (via :meth:`remove_pointer_callback()`) or automatically (if + the scene pointer event is overridden with another call to + :meth:`on_pointer_event()`). + + Args: + func: Callback for when scene pointer events are removed. + """ + self._scene_pointer_done_cb = func + return func
+ + +
+[docs] + def remove_pointer_callback( + self, + ) -> None: + """Remove the currently attached scene pointer event. This will trigger + any callback attached to `.on_scene_pointer_removed()`.""" + + if self._scene_pointer_cb is None: + warnings.warn( + "No scene pointer callback exists for this server/client, ignoring.", + stacklevel=2, + ) + return + + # Notify client that the listener has been removed. + event_type = self._scene_pointer_event_type + assert event_type is not None + self._websock_interface.queue_message( + _messages.ScenePointerEnableMessage(enable=False, event_type=event_type) + ) + self._owner.flush() + + # Run cleanup callback. + self._scene_pointer_done_cb() + + # Reset the callback and event type, on the python side. + self._scene_pointer_cb = None + self._scene_pointer_done_cb = lambda: None + self._scene_pointer_event_type = None
+ + +
+[docs] + def add_3d_gui_container( + self, + name: str, + wxyz: tuple[float, float, float, float] | onp.ndarray = (1.0, 0.0, 0.0, 0.0), + position: tuple[float, float, float] | onp.ndarray = (0.0, 0.0, 0.0), + visible: bool = True, + ) -> Gui3dContainerHandle: + """Add a 3D gui container to the scene. The returned container handle can be + used as a context to place GUI elements into the 3D scene. + + Args: + name: A scene tree name. Names in the format of /parent/child can be used to + define a kinematic tree. + wxyz: Quaternion rotation to parent frame from local frame (R_pl). + position: Translation to parent frame from local frame (t_pl). + visible: Whether or not this scene node is initially visible. + + Returns: + Handle for manipulating scene node. Can be used as a context to place GUI + elements inside of the container. + """ + + # Avoids circular import. + from ._gui_api import _make_unique_id + + # New name to make the type checker happy; ViserServer and ClientHandle inherit + # from both GuiApi and MessageApi. The pattern below is unideal. + gui_api = self._owner.gui + + # Remove the 3D GUI container if it already exists. This will make sure + # contained GUI elements are removed, preventing potential memory leaks. + if name in self._handle_from_node_name: + self._handle_from_node_name[name].remove() + + container_id = _make_unique_id() + self._websock_interface.queue_message( + _messages.Gui3DMessage( + order=time.time(), + name=name, + container_id=container_id, + ) + ) + node_handle = SceneNodeHandle._make(self, name, wxyz, position, visible=visible) + return Gui3dContainerHandle(node_handle._impl, gui_api, container_id)
+
+ +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/_scene_handles/index.html b/versions/0.2.2/_modules/viser/_scene_handles/index.html new file mode 100644 index 000000000..e05d0eb4f --- /dev/null +++ b/versions/0.2.2/_modules/viser/_scene_handles/index.html @@ -0,0 +1,682 @@ + + + + + + + + + + + + viser._scene_handles - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser._scene_handles

+from __future__ import annotations
+
+import dataclasses
+from typing import TYPE_CHECKING, Callable, Generic, Literal, TypeVar
+
+import numpy as onp
+
+from . import _messages
+
+if TYPE_CHECKING:
+    from ._gui_api import GuiApi
+    from ._gui_handles import SupportsRemoveProtocol
+    from ._scene_api import SceneApi
+    from ._viser import ClientHandle
+    from .infra import ClientId
+
+
+
+[docs] +@dataclasses.dataclass(frozen=True) +class ScenePointerEvent: + """Event passed to pointer callbacks for the scene (currently only clicks).""" + + client: ClientHandle + """Client that triggered this event.""" + client_id: int + """ID of client that triggered this event.""" + event_type: _messages.ScenePointerEventType + """Type of event that was triggered. Currently we only support clicks and box selections.""" + ray_origin: tuple[float, float, float] | None + """Origin of 3D ray corresponding to this click, in world coordinates.""" + ray_direction: tuple[float, float, float] | None + """Direction of 3D ray corresponding to this click, in world coordinates.""" + screen_pos: list[tuple[float, float]] + """Screen position of the click on the screen (OpenCV image coordinates, 0 to 1). + (0, 0) is the upper-left corner, (1, 1) is the bottom-right corner. + For a box selection, this includes the min- and max- corners of the box.""" + + @property + def event(self): + """Deprecated. Use `event_type` instead.""" + return self.event_type
+ + + +TSceneNodeHandle = TypeVar("TSceneNodeHandle", bound="SceneNodeHandle") + + +@dataclasses.dataclass +class _SceneNodeHandleState: + name: str + api: SceneApi + wxyz: onp.ndarray = dataclasses.field( + default_factory=lambda: onp.array([1.0, 0.0, 0.0, 0.0]) + ) + position: onp.ndarray = dataclasses.field( + default_factory=lambda: onp.array([0.0, 0.0, 0.0]) + ) + visible: bool = True + # TODO: we should remove SceneNodeHandle as an argument here. + click_cb: list[Callable[[SceneNodePointerEvent[SceneNodeHandle]], None]] | None = ( + None + ) + + +
+[docs] +@dataclasses.dataclass +class SceneNodeHandle: + """Handle base class for interacting with scene nodes.""" + + _impl: _SceneNodeHandleState + + @classmethod + def _make( + cls: type[TSceneNodeHandle], + api: SceneApi, + name: str, + wxyz: tuple[float, float, float, float] | onp.ndarray, + position: tuple[float, float, float] | onp.ndarray, + visible: bool, + ) -> TSceneNodeHandle: + out = cls(_SceneNodeHandleState(name, api)) + api._handle_from_node_name[name] = out + + out.wxyz = wxyz + out.position = position + + # Toggle visibility to make sure we send a + # SetSceneNodeVisibilityMessage to the client. + out._impl.visible = not visible + out.visible = visible + return out + + @property + def wxyz(self) -> onp.ndarray: + """Orientation of the scene node. This is the quaternion representation of the R + in `p_parent = [R | t] p_local`. Synchronized to clients automatically when assigned. + """ + return self._impl.wxyz + + @wxyz.setter + def wxyz(self, wxyz: tuple[float, float, float, float] | onp.ndarray) -> None: + from ._scene_api import cast_vector + + wxyz_cast = cast_vector(wxyz, 4) + self._impl.wxyz = onp.asarray(wxyz) + self._impl.api._websock_interface.queue_message( + _messages.SetOrientationMessage(self._impl.name, wxyz_cast) + ) + + @property + def position(self) -> onp.ndarray: + """Position of the scene node. This is equivalent to the t in + `p_parent = [R | t] p_local`. Synchronized to clients automatically when assigned. + """ + return self._impl.position + + @position.setter + def position(self, position: tuple[float, float, float] | onp.ndarray) -> None: + from ._scene_api import cast_vector + + position_cast = cast_vector(position, 3) + self._impl.position = onp.asarray(position) + self._impl.api._websock_interface.queue_message( + _messages.SetPositionMessage(self._impl.name, position_cast) + ) + + @property + def visible(self) -> bool: + """Whether the scene node is visible or not. Synchronized to clients automatically when assigned.""" + return self._impl.visible + + @visible.setter + def visible(self, visible: bool) -> None: + if visible == self._impl.visible: + return + self._impl.api._websock_interface.queue_message( + _messages.SetSceneNodeVisibilityMessage(self._impl.name, visible) + ) + self._impl.visible = visible + +
+[docs] + def remove(self) -> None: + """Remove the node from the scene.""" + self._impl.api._websock_interface.queue_message( + _messages.RemoveSceneNodeMessage(self._impl.name) + )
+
+ + + +
+[docs] +@dataclasses.dataclass(frozen=True) +class SceneNodePointerEvent(Generic[TSceneNodeHandle]): + """Event passed to pointer callbacks for scene nodes (currently only clicks).""" + + client: ClientHandle + """Client that triggered this event.""" + client_id: int + """ID of client that triggered this event.""" + event: Literal["click"] + """Type of event that was triggered. Currently we only support clicks.""" + target: TSceneNodeHandle + """Scene node that was clicked.""" + ray_origin: tuple[float, float, float] + """Origin of 3D ray corresponding to this click, in world coordinates.""" + ray_direction: tuple[float, float, float] + """Direction of 3D ray corresponding to this click, in world coordinates."""
+ + + +@dataclasses.dataclass +class _ClickableSceneNodeHandle(SceneNodeHandle): + def on_click( + self: TSceneNodeHandle, + func: Callable[[SceneNodePointerEvent[TSceneNodeHandle]], None], + ) -> Callable[[SceneNodePointerEvent[TSceneNodeHandle]], None]: + """Attach a callback for when a scene node is clicked.""" + self._impl.api._websock_interface.queue_message( + _messages.SetSceneNodeClickableMessage(self._impl.name, True) + ) + if self._impl.click_cb is None: + self._impl.click_cb = [] + self._impl.click_cb.append(func) # type: ignore + return func + + +
+[docs] +@dataclasses.dataclass +class CameraFrustumHandle(_ClickableSceneNodeHandle): + """Handle for camera frustums."""
+ + + +
+[docs] +@dataclasses.dataclass +class PointCloudHandle(SceneNodeHandle): + """Handle for point clouds. Does not support click events."""
+ + + +
+[docs] +@dataclasses.dataclass +class BatchedAxesHandle(_ClickableSceneNodeHandle): + """Handle for batched coordinate frames."""
+ + + +
+[docs] +@dataclasses.dataclass +class FrameHandle(_ClickableSceneNodeHandle): + """Handle for coordinate frames."""
+ + + +
+[docs] +@dataclasses.dataclass +class MeshHandle(_ClickableSceneNodeHandle): + """Handle for mesh objects."""
+ + + +
+[docs] +@dataclasses.dataclass +class GlbHandle(_ClickableSceneNodeHandle): + """Handle for GLB objects."""
+ + + +
+[docs] +@dataclasses.dataclass +class ImageHandle(_ClickableSceneNodeHandle): + """Handle for 2D images, rendered in 3D."""
+ + + +
+[docs] +@dataclasses.dataclass +class LabelHandle(SceneNodeHandle): + """Handle for 2D label objects. Does not support click events."""
+ + + +@dataclasses.dataclass +class _TransformControlsState: + last_updated: float + update_cb: list[Callable[[TransformControlsHandle], None]] + sync_cb: None | Callable[[ClientId, TransformControlsHandle], None] = None + + +
+[docs] +@dataclasses.dataclass +class TransformControlsHandle(_ClickableSceneNodeHandle): + """Handle for interacting with transform control gizmos.""" + + _impl_aux: _TransformControlsState + + @property + def update_timestamp(self) -> float: + return self._impl_aux.last_updated + +
+[docs] + def on_update( + self, func: Callable[[TransformControlsHandle], None] + ) -> Callable[[TransformControlsHandle], None]: + """Attach a callback for when the gizmo is moved.""" + self._impl_aux.update_cb.append(func) + return func
+
+ + + +
+[docs] +@dataclasses.dataclass +class Gui3dContainerHandle(SceneNodeHandle): + """Use as a context to place GUI elements into a 3D GUI container.""" + + _gui_api: GuiApi + _container_id: str + _container_id_restore: str | None = None + _children: dict[str, SupportsRemoveProtocol] = dataclasses.field( + default_factory=dict + ) + + def __enter__(self) -> Gui3dContainerHandle: + self._container_id_restore = self._gui_api._get_container_id() + self._gui_api._set_container_id(self._container_id) + return self + + def __exit__(self, *args) -> None: + del args + assert self._container_id_restore is not None + self._gui_api._set_container_id(self._container_id_restore) + self._container_id_restore = None + + def __post_init__(self) -> None: + self._gui_api._container_handle_from_id[self._container_id] = self + +
+[docs] + def remove(self) -> None: + """Permanently remove this GUI container from the visualizer.""" + + # Call scene node remove. + super().remove() + + # Clean up contained GUI elements. + for child in tuple(self._children.values()): + child.remove() + self._gui_api._container_handle_from_id.pop(self._container_id)
+
+ +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/_viser/index.html b/versions/0.2.2/_modules/viser/_viser/index.html new file mode 100644 index 000000000..0ce8518a7 --- /dev/null +++ b/versions/0.2.2/_modules/viser/_viser/index.html @@ -0,0 +1,1104 @@ + + + + + + + + + + + + viser._viser - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser._viser

+from __future__ import annotations
+
+import dataclasses
+import io
+import mimetypes
+import threading
+import time
+import warnings
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Callable, ContextManager
+
+import imageio.v3 as iio
+import numpy as onp
+import numpy.typing as npt
+import rich
+from rich import box, style
+from rich.panel import Panel
+from rich.table import Table
+from typing_extensions import Literal
+
+from . import _client_autobuild, _messages, infra
+from . import transforms as tf
+from ._gui_api import GuiApi
+from ._scene_api import SceneApi, cast_vector
+from ._tunnel import ViserTunnel
+
+
+class _BackwardsCompatibilityShim:
+    """Shims for backward compatibility with viser API from version
+    `<=0.1.30`."""
+
+    def __getattr__(self, name: str) -> Any:
+        fixed_name = {
+            # Map from old method names (viser v0.1.*) to new methods names.
+            "reset_scene": "reset",
+            "set_global_scene_node_visibility": "set_global_visibility",
+            "on_scene_pointer": "on_pointer_event",
+            "on_scene_pointer_removed": "on_pointer_callback_removed",
+            "remove_scene_pointer_callback": "remove_pointer_callback",
+            "add_mesh": "add_mesh_simple",
+        }.get(name, name)
+        if hasattr(self.scene, fixed_name):
+            warnings.warn(
+                f"{type(self).__name__}.{name} has been deprecated, use {type(self).__name__}.scene.{fixed_name} instead. Alternatively, pin to `viser<0.2.0`.",
+                category=DeprecationWarning,
+                stacklevel=2,
+            )
+            return object.__getattribute__(self.scene, fixed_name)
+
+        fixed_name = name.replace("add_gui_", "add_").replace("set_gui_", "set_")
+        if hasattr(self.gui, fixed_name):
+            warnings.warn(
+                f"{type(self).__name__}.{name} has been deprecated, use {type(self).__name__}.gui.{fixed_name} instead. Alternatively, pin to `viser<0.2.0`.",
+                category=DeprecationWarning,
+                stacklevel=2,
+            )
+            return object.__getattribute__(self.gui, fixed_name)
+
+        raise AttributeError(
+            f"'{type(self).__name__}' object has no attribute '{name}'"
+        )
+
+
+@dataclasses.dataclass
+class _CameraHandleState:
+    """Information about a client's camera state."""
+
+    client: ClientHandle
+    wxyz: npt.NDArray[onp.float64]
+    position: npt.NDArray[onp.float64]
+    fov: float
+    aspect: float
+    look_at: npt.NDArray[onp.float64]
+    up_direction: npt.NDArray[onp.float64]
+    update_timestamp: float
+    camera_cb: list[Callable[[CameraHandle], None]]
+
+
+
+[docs] +class CameraHandle: + """A handle for reading and writing the camera state of a particular + client. Typically accessed via :attr:`ClientHandle.camera`.""" + + def __init__(self, client: ClientHandle) -> None: + self._state = _CameraHandleState( + client, + wxyz=onp.zeros(4), + position=onp.zeros(3), + fov=0.0, + aspect=0.0, + look_at=onp.zeros(3), + up_direction=onp.zeros(3), + update_timestamp=0.0, + camera_cb=[], + ) + + @property + def client(self) -> ClientHandle: + """Client that this camera corresponds to.""" + return self._state.client + + @property + def wxyz(self) -> npt.NDArray[onp.float64]: + """Corresponds to the R in `P_world = [R | t] p_camera`. Synchronized + automatically when assigned.""" + assert self._state.update_timestamp != 0.0 + return self._state.wxyz + + # Note: asymmetric properties are supported in Pyright, but not yet in mypy. + # - https://github.com/python/mypy/issues/3004 + # - https://github.com/python/mypy/pull/11643 + @wxyz.setter + def wxyz(self, wxyz: tuple[float, float, float, float] | onp.ndarray) -> None: + R_world_camera = tf.SO3(onp.asarray(wxyz)).as_matrix() + look_distance = onp.linalg.norm(self.look_at - self.position) + + # We're following OpenCV conventions: look_direction is +Z, up_direction is -Y, + # right_direction is +X. + look_direction = R_world_camera[:, 2] + up_direction = -R_world_camera[:, 1] + right_direction = R_world_camera[:, 0] + + # Minimize our impact on the orbit controls by keeping the new up direction as + # close to the old one as possible. + projected_up_direction = ( + self.up_direction + - float(self.up_direction @ right_direction) * right_direction + ) + up_cosine = float(up_direction @ projected_up_direction) + if abs(up_cosine) < 0.05: + projected_up_direction = up_direction + elif up_cosine < 0.0: + projected_up_direction = up_direction + + new_look_at = look_direction * look_distance + self.position + + # Update lookat and up direction. + self.look_at = new_look_at + self.up_direction = projected_up_direction + + # The internal camera orientation should be set in the look_at / + # up_direction setters. We can uncomment this assert to check this. + # assert onp.allclose(self._state.wxyz, wxyz) or onp.allclose( + # self._state.wxyz, -wxyz + # ) + + @property + def position(self) -> npt.NDArray[onp.float64]: + """Corresponds to the t in `P_world = [R | t] p_camera`. Synchronized + automatically when assigned. + + The `look_at` point and `up_direction` vectors are maintained when updating + `position`, which means that updates to `position` will often also affect `wxyz`. + """ + assert self._state.update_timestamp != 0.0 + return self._state.position + + @position.setter + def position(self, position: tuple[float, float, float] | onp.ndarray) -> None: + offset = onp.asarray(position) - onp.array(self.position) # type: ignore + self._state.position = onp.asarray(position) + self.look_at = onp.array(self.look_at) + offset + self._state.update_timestamp = time.time() + self._state.client._websock_connection.queue_message( + _messages.SetCameraPositionMessage(cast_vector(position, 3)) + ) + + def _update_wxyz(self) -> None: + """Compute and update the camera orientation from the internal look_at, position, and up vectors.""" + z = self._state.look_at - self._state.position + z /= onp.linalg.norm(z) + y = tf.SO3.exp(z * onp.pi) @ self._state.up_direction + y = y - onp.dot(z, y) * z + y /= onp.linalg.norm(y) + x = onp.cross(y, z) + self._state.wxyz = tf.SO3.from_matrix(onp.stack([x, y, z], axis=1)).wxyz + + @property + def fov(self) -> float: + """Vertical field of view of the camera, in radians. Synchronized automatically + when assigned.""" + assert self._state.update_timestamp != 0.0 + return self._state.fov + + @fov.setter + def fov(self, fov: float) -> None: + self._state.fov = fov + self._state.update_timestamp = time.time() + self._state.client._websock_connection.queue_message( + _messages.SetCameraFovMessage(fov) + ) + + @property + def aspect(self) -> float: + """Canvas width divided by height. Not assignable.""" + assert self._state.update_timestamp != 0.0 + return self._state.aspect + + @property + def update_timestamp(self) -> float: + assert self._state.update_timestamp != 0.0 + return self._state.update_timestamp + + @property + def look_at(self) -> npt.NDArray[onp.float64]: + """Look at point for the camera. Synchronized automatically when set.""" + assert self._state.update_timestamp != 0.0 + return self._state.look_at + + @look_at.setter + def look_at(self, look_at: tuple[float, float, float] | onp.ndarray) -> None: + self._state.look_at = onp.asarray(look_at) + self._state.update_timestamp = time.time() + self._update_wxyz() + self._state.client._websock_connection.queue_message( + _messages.SetCameraLookAtMessage(cast_vector(look_at, 3)) + ) + + @property + def up_direction(self) -> npt.NDArray[onp.float64]: + """Up direction for the camera. Synchronized automatically when set.""" + assert self._state.update_timestamp != 0.0 + return self._state.up_direction + + @up_direction.setter + def up_direction( + self, up_direction: tuple[float, float, float] | onp.ndarray + ) -> None: + self._state.up_direction = onp.asarray(up_direction) + self._update_wxyz() + self._state.update_timestamp = time.time() + self._state.client._websock_connection.queue_message( + _messages.SetCameraUpDirectionMessage(cast_vector(up_direction, 3)) + ) + +
+[docs] + def on_update( + self, callback: Callable[[CameraHandle], None] + ) -> Callable[[CameraHandle], None]: + """Attach a callback to run when a new camera message is received.""" + self._state.camera_cb.append(callback) + return callback
+ + +
+[docs] + def get_render( + self, height: int, width: int, transport_format: Literal["png", "jpeg"] = "jpeg" + ) -> onp.ndarray: + """Request a render from a client, block until it's done and received, then + return it as a numpy array. + + Args: + height: Height of rendered image. Should be <= the browser height. + width: Width of rendered image. Should be <= the browser width. + transport_format: Image transport format. JPEG will return a lossy (H, W, 3) RGB array. PNG will + return a lossless (H, W, 4) RGBA array, but can cause memory issues on the frontend if called + too quickly for higher-resolution images. + """ + + # Listen for a render reseponse message, which should contain the rendered + # image. + render_ready_event = threading.Event() + out: onp.ndarray | None = None + + connection = self.client._websock_connection + + def got_render_cb( + client_id: int, message: _messages.GetRenderResponseMessage + ) -> None: + del client_id + connection.unregister_handler( + _messages.GetRenderResponseMessage, got_render_cb + ) + nonlocal out + out = iio.imread( + io.BytesIO(message.payload), + extension=f".{transport_format}", + ) + render_ready_event.set() + + connection.register_handler(_messages.GetRenderResponseMessage, got_render_cb) + self.client._websock_connection.queue_message( + _messages.GetRenderRequestMessage( + "image/jpeg" if transport_format == "jpeg" else "image/png", + height=height, + width=width, + # Only used for JPEG. The main reason to use a lower quality version + # value is (unfortunately) to make life easier for the Javascript + # garbage collector. + quality=80, + ) + ) + render_ready_event.wait() + assert out is not None + return out
+
+ + + +# Don't inherit from _BackwardsCompatibilityShim during type checking, because +# this will unnecessarily suppress type errors. (from the overriding of +# __getattr__). +
+[docs] +class ClientHandle(_BackwardsCompatibilityShim if not TYPE_CHECKING else object): + """A handle is created for each client that connects to a server. Handles can be + used to communicate with just one client, as well as for reading and writing of + camera state. + + Similar to :class:`ViserServer`, client handles also expose scene and GUI + interfaces at :attr:`ClientHandle.scene` and :attr:`ClientHandle.gui`. If + these are used, for example via a client's + :meth:`SceneApi.add_point_cloud()` method, created elements are local to + only one specific client. + """ + + def __init__( + self, conn: infra.WebsockClientConnection, server: ViserServer + ) -> None: + # Private attributes. + self._websock_connection = conn + self._viser_server = server + + # Public attributes. + self.scene: SceneApi = SceneApi( + self, thread_executor=server._websock_server._thread_executor + ) + """Handle for interacting with the 3D scene.""" + self.gui: GuiApi = GuiApi( + self, thread_executor=server._websock_server._thread_executor + ) + """Handle for interacting with the GUI.""" + self.client_id: int = conn.client_id + """Unique ID for this client.""" + self.camera: CameraHandle = CameraHandle(self) + """Handle for reading from and manipulating the client's viewport camera.""" + +
+[docs] + def flush(self) -> None: + """Flush the outgoing message buffer. Any buffered messages will immediately be + sent. (by default they are windowed)""" + self._viser_server._websock_server.flush_client(self.client_id)
+ + +
+[docs] + def atomic(self) -> ContextManager[None]: + """Returns a context where: all outgoing messages are grouped and applied by + clients atomically. + + This should be treated as a soft constraint that's helpful for things + like animations, or when we want position and orientation updates to + happen synchronously. + + Returns: + Context manager. + """ + return self._websock_connection.atomic()
+ + +
+[docs] + def send_file_download( + self, filename: str, content: bytes, chunk_size: int = 1024 * 1024 + ) -> None: + """Send a file for a client or clients to download. + + Args: + filename: Name of the file to send. Used to infer MIME type. + content: Content of the file. + chunk_size: Number of bytes to send at a time. + """ + mime_type = mimetypes.guess_type(filename, strict=False)[0] + if mime_type is None: + mime_type = "application/octet-stream" + + from ._gui_api import _make_unique_id + + parts = [ + content[i * chunk_size : (i + 1) * chunk_size] + for i in range(int(onp.ceil(len(content) / chunk_size))) + ] + + uuid = _make_unique_id() + self._websock_connection.queue_message( + _messages.FileTransferStart( + source_component_id=None, + transfer_uuid=uuid, + filename=filename, + mime_type=mime_type, + part_count=len(parts), + size_bytes=len(content), + ) + ) + for i, part in enumerate(parts): + self._websock_connection.queue_message( + _messages.FileTransferPart( + None, + transfer_uuid=uuid, + part=i, + content=part, + ) + ) + self.flush()
+
+ + + +
+[docs] +class ViserServer(_BackwardsCompatibilityShim if not TYPE_CHECKING else object): + """:class:`ViserServer` is the main class for working with viser. On + instantiation, it (a) launches a thread with a web server and (b) provides + a high-level API for interactive 3D visualization. + + **Core API.** Clients can connect via a web browser, and will be shown two + components: a 3D scene and a 2D GUI panel. Methods belonging to + :attr:`ViserServer.scene` can be used to add 3D primitives to the scene. + Methods belonging to :attr:`ViserServer.gui` can be used to add 2D GUI + elements. + + **Shared state.** Elements added to the server object, for example via a + server's :meth:`SceneApi.add_point_cloud` or :meth:`GuiApi.add_button`, + will have state that's shared and synchronized automatically between all + connected clients. To show elements that are local to a single client, see + :attr:`ClientHandle.scene` and :attr:`ClientHandle.gui`. + + Args: + host: Host to bind server to. + port: Port to bind server to. + label: Label shown at the top of the GUI panel. + """ + + # Hide deprecated arguments from docstring and type checkers. + def __init__( + self, + host: str = "0.0.0.0", + port: int = 8080, + label: str | None = None, + verbose: bool = True, + **_deprecated_kwargs, + ): + # Create server. + server = infra.WebsockServer( + host=host, + port=port, + message_class=_messages.Message, + http_server_root=Path(__file__).absolute().parent / "client" / "build", + verbose=verbose, + client_api_version=1, + ) + self._websock_server = server + + _client_autobuild.ensure_client_is_built() + + self._connection = server + self._connected_clients: dict[int, ClientHandle] = {} + self._client_lock = threading.Lock() + self._client_connect_cb: list[Callable[[ClientHandle], None]] = [] + self._client_disconnect_cb: list[Callable[[ClientHandle], None]] = [] + + # For new clients, register and add a handler for camera messages. + @server.on_client_connect + def _(conn: infra.WebsockClientConnection) -> None: + client = ClientHandle(conn, server=self) + first = True + + def handle_camera_message( + client_id: infra.ClientId, message: _messages.ViewerCameraMessage + ) -> None: + nonlocal first + + assert client_id == client.client_id + + # Update the client's camera. + with client.atomic(): + client.camera._state = _CameraHandleState( + client, + onp.array(message.wxyz), + onp.array(message.position), + message.fov, + message.aspect, + onp.array(message.look_at), + onp.array(message.up_direction), + time.time(), + camera_cb=client.camera._state.camera_cb, + ) + + # We consider a client to be connected after the first camera message is + # received. + if first: + first = False + with self._client_lock: + self._connected_clients[conn.client_id] = client + for cb in self._client_connect_cb: + cb(client) + + for camera_cb in client.camera._state.camera_cb: + camera_cb(client.camera) + + conn.register_handler(_messages.ViewerCameraMessage, handle_camera_message) + + # Remove clients when they disconnect. + @server.on_client_disconnect + def _(conn: infra.WebsockClientConnection) -> None: + with self._client_lock: + if conn.client_id not in self._connected_clients: + return + + handle = self._connected_clients.pop(conn.client_id) + for cb in self._client_disconnect_cb: + cb(handle) + + # Start the server. + server.start() + + self.scene: SceneApi = SceneApi(self, thread_executor=server._thread_executor) + """Handle for interacting with the 3D scene.""" + + self.gui: GuiApi = GuiApi(self, thread_executor=server._thread_executor) + """Handle for interacting with the GUI.""" + + server.register_handler( + _messages.ShareUrlDisconnect, + lambda client_id, msg: self.disconnect_share_url(), + ) + server.register_handler( + _messages.ShareUrlRequest, lambda client_id, msg: self.request_share_url() + ) + + # Form status print. + port = server._port # Port may have changed. + http_url = f"http://{host}:{port}" + ws_url = f"ws://{host}:{port}" + table = Table( + title=None, + show_header=False, + box=box.MINIMAL, + title_style=style.Style(bold=True), + ) + table.add_row("HTTP", http_url) + table.add_row("Websocket", ws_url) + rich.print(Panel(table, title="[bold]viser[/bold]", expand=False)) + + self._share_tunnel: ViserTunnel | None = None + + # Create share tunnel if requested. + # This is deprecated: we should use get_share_url() instead. + share = _deprecated_kwargs.get("share", False) + if share: + self.request_share_url() + + self.scene.reset() + self.gui.set_panel_label(label) + +
+[docs] + def get_host(self) -> str: + """Returns the host address of the Viser server. + + Returns: + Host address as string. + """ + return self._websock_server._host
+ + +
+[docs] + def get_port(self) -> int: + """Returns the port of the Viser server. This could be different from the + originally requested one. + + Returns: + Port as integer. + """ + return self._websock_server._port
+ + +
+[docs] + def request_share_url(self, verbose: bool = True) -> str | None: + """Request a share URL for the Viser server, which allows for public access. + On the first call, will block until a connecting with the share URL server is + established. Afterwards, the URL will be returned directly. + + This is an experimental feature that relies on an external server; it shouldn't + be relied on for critical applications. + + Returns: + Share URL as string, or None if connection fails or is closed. + """ + if self._share_tunnel is not None: + # Tunnel already exists. + while self._share_tunnel.get_status() in ("ready", "connecting"): + time.sleep(0.05) + return self._share_tunnel.get_url() + else: + # Create a new tunnel!. + if verbose: + rich.print("[bold](viser)[/bold] Share URL requested!") + + connect_event = threading.Event() + + self._share_tunnel = ViserTunnel( + "share.viser.studio", self._websock_server._port + ) + + @self._share_tunnel.on_disconnect + def _() -> None: + rich.print("[bold](viser)[/bold] Disconnected from share URL") + self._share_tunnel = None + self._websock_server.unsafe_send_message( + _messages.ShareUrlUpdated(None) + ) + + @self._share_tunnel.on_connect + def _(max_clients: int) -> None: + assert self._share_tunnel is not None + share_url = self._share_tunnel.get_url() + if verbose: + if share_url is None: + rich.print("[bold](viser)[/bold] Could not generate share URL") + else: + rich.print( + f"[bold](viser)[/bold] Generated share URL (expires in 24 hours, max {max_clients} clients): {share_url}" + ) + self._websock_server.unsafe_send_message( + _messages.ShareUrlUpdated(share_url) + ) + connect_event.set() + + connect_event.wait() + + url = self._share_tunnel.get_url() + return url
+ + +
+[docs] + def disconnect_share_url(self) -> None: + """Disconnect from the share URL server.""" + if self._share_tunnel is not None: + self._share_tunnel.close() + else: + rich.print( + "[bold](viser)[/bold] Tried to disconnect from share URL, but already disconnected" + )
+ + +
+[docs] + def stop(self) -> None: + """Stop the Viser server and associated threads and tunnels.""" + self._websock_server.stop() + if self._share_tunnel is not None: + self._share_tunnel.close()
+ + +
+[docs] + def get_clients(self) -> dict[int, ClientHandle]: + """Creates and returns a copy of the mapping from connected client IDs to + handles. + + Returns: + Dictionary of clients. + """ + with self._client_lock: + return self._connected_clients.copy()
+ + +
+[docs] + def on_client_connect( + self, cb: Callable[[ClientHandle], None] + ) -> Callable[[ClientHandle], None]: + """Attach a callback to run for newly connected clients.""" + with self._client_lock: + clients = self._connected_clients.copy().values() + self._client_connect_cb.append(cb) + + # Trigger callback on any already-connected clients. + # If we have: + # + # server = viser.ViserServer() + # server.on_client_connect(...) + # + # This makes sure that the the callback is applied to any clients that + # connect between the two lines. + for client in clients: + cb(client) + return cb
+ + +
+[docs] + def on_client_disconnect( + self, cb: Callable[[ClientHandle], None] + ) -> Callable[[ClientHandle], None]: + """Attach a callback to run when clients disconnect.""" + self._client_disconnect_cb.append(cb) + return cb
+ + +
+[docs] + def flush(self) -> None: + """Flush the outgoing message buffer. Any buffered messages will immediately be + sent. (by default they are windowed)""" + self._websock_server.flush()
+ + +
+[docs] + def atomic(self) -> ContextManager[None]: + """Returns a context where: all outgoing messages are grouped and applied by + clients atomically. + + This should be treated as a soft constraint that's helpful for things + like animations, or when we want position and orientation updates to + happen synchronously. + + Returns: + Context manager. + """ + return self._websock_server.atomic()
+ + +
+[docs] + def send_file_download( + self, filename: str, content: bytes, chunk_size: int = 1024 * 1024 + ) -> None: + """Send a file for a client or clients to download. + + Args: + filename: Name of the file to send. Used to infer MIME type. + content: Content of the file. + chunk_size: Number of bytes to send at a time. + """ + for client in self.get_clients().values(): + client.send_file_download(filename, content, chunk_size)
+
+ +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/extras/_record3d/index.html b/versions/0.2.2/_modules/viser/extras/_record3d/index.html new file mode 100644 index 000000000..0201bc3bb --- /dev/null +++ b/versions/0.2.2/_modules/viser/extras/_record3d/index.html @@ -0,0 +1,498 @@ + + + + + + + + + + + + viser.extras._record3d - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser.extras._record3d

+from __future__ import annotations
+
+import dataclasses
+import json
+from pathlib import Path
+from typing import Tuple, cast
+
+import imageio.v3 as iio
+import liblzfse
+import numpy as np
+import numpy as onp
+import numpy.typing as onpt
+import skimage.transform
+from scipy.spatial.transform import Rotation
+
+
+
+[docs] +class Record3dLoader: + """Helper for loading frames for Record3D captures.""" + + # NOTE(hangg): Consider moving this module into + # `examples/7_record3d_visualizer.py` since it is usecase-specific. + + def __init__(self, data_dir: Path): + metadata_path = data_dir / "metadata" + + # Read metadata. + metadata = json.loads(metadata_path.read_text()) + + K: onp.ndarray = np.array(metadata["K"], np.float32).reshape(3, 3).T + fps = metadata["fps"] + + T_world_cameras: onp.ndarray = np.array(metadata["poses"], np.float32) + T_world_cameras = np.concatenate( + [ + Rotation.from_quat(T_world_cameras[:, :4]).as_matrix(), + T_world_cameras[:, 4:, None], + ], + -1, + ) + T_world_cameras = (T_world_cameras @ np.diag([1, -1, -1, 1])).astype(np.float32) + + self.K = K + self.fps = fps + self.T_world_cameras = T_world_cameras + + rgbd_dir = data_dir / "rgbd" + self.rgb_paths = sorted(rgbd_dir.glob("*.jpg"), key=lambda p: int(p.stem)) + self.depth_paths = [ + rgb_path.with_suffix(".depth") for rgb_path in self.rgb_paths + ] + self.conf_paths = [rgb_path.with_suffix(".conf") for rgb_path in self.rgb_paths] + +
+[docs] + def num_frames(self) -> int: + return len(self.rgb_paths)
+ + +
+[docs] + def get_frame(self, index: int) -> Record3dFrame: + # Read conf. + conf: onp.ndarray = np.frombuffer( + liblzfse.decompress(self.conf_paths[index].read_bytes()), dtype=np.uint8 + ) + if conf.shape[0] == 640 * 480: + conf = conf.reshape((640, 480)) # For a FaceID camera 3D Video + elif conf.shape[0] == 256 * 192: + conf = conf.reshape((256, 192)) # For a LiDAR 3D Video + else: + assert False, f"Unexpected conf shape {conf.shape}" + + # Read depth. + depth: onp.ndarray = np.frombuffer( + liblzfse.decompress(self.depth_paths[index].read_bytes()), dtype=np.float32 + ).copy() + if depth.shape[0] == 640 * 480: + depth = depth.reshape((640, 480)) # For a FaceID camera 3D Video + elif depth.shape[0] == 256 * 192: + depth = depth.reshape((256, 192)) # For a LiDAR 3D Video + else: + assert False, f"Unexpected depth shape {depth.shape}" + + # Read RGB. + rgb = iio.imread(self.rgb_paths[index]) + return Record3dFrame( + K=self.K, + rgb=rgb, + depth=depth, + mask=conf == 2, + T_world_camera=self.T_world_cameras[index], + )
+
+ + + +
+[docs] +@dataclasses.dataclass +class Record3dFrame: + """A single frame from a Record3D capture.""" + + K: onpt.NDArray[onp.float32] + rgb: onpt.NDArray[onp.uint8] + depth: onpt.NDArray[onp.float32] + mask: onpt.NDArray[onp.bool_] + T_world_camera: onpt.NDArray[onp.float32] + +
+[docs] + def get_point_cloud( + self, downsample_factor: int = 1 + ) -> Tuple[onpt.NDArray[onp.float32], onpt.NDArray[onp.uint8]]: + rgb = self.rgb[::downsample_factor, ::downsample_factor] + depth = skimage.transform.resize(self.depth, rgb.shape[:2], order=0) + mask = cast( + onpt.NDArray[onp.bool_], + skimage.transform.resize(self.mask, rgb.shape[:2], order=0), + ) + assert depth.shape == rgb.shape[:2] + + K = self.K + T_world_camera = self.T_world_camera + + img_wh = rgb.shape[:2][::-1] + + grid = ( + np.stack(np.meshgrid(np.arange(img_wh[0]), np.arange(img_wh[1])), 2) + 0.5 + ) + grid = grid * downsample_factor + + homo_grid = np.pad(grid[mask], np.array([[0, 0], [0, 1]]), constant_values=1) + local_dirs = np.einsum("ij,bj->bi", np.linalg.inv(K), homo_grid) + dirs = np.einsum("ij,bj->bi", T_world_camera[:3, :3], local_dirs) + points = (T_world_camera[:, -1] + dirs * depth[mask, None]).astype(np.float32) + point_colors = rgb[mask] + + return points, point_colors
+
+ +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/extras/_urdf/index.html b/versions/0.2.2/_modules/viser/extras/_urdf/index.html new file mode 100644 index 000000000..dc0a6a806 --- /dev/null +++ b/versions/0.2.2/_modules/viser/extras/_urdf/index.html @@ -0,0 +1,526 @@ + + + + + + + + + + + + viser.extras._urdf - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser.extras._urdf

+from __future__ import annotations
+
+from functools import partial
+from pathlib import Path
+from typing import List, Tuple
+
+import numpy as onp
+import trimesh
+import yourdfpy
+
+import viser
+
+from .. import transforms as tf
+
+
+
+[docs] +class ViserUrdf: + """Helper for rendering URDFs in Viser. + + Args: + target: ViserServer or ClientHandle object to add URDF to. + urdf_or_path: Either a path to a URDF file or a yourdfpy URDF object. + scale: Scale factor to apply to resize the URDF. + root_node_name: Viser scene tree name for the root of the URDF geometry. + mesh_color_override: Optional color to override the URDF's mesh colors. + """ + + def __init__( + self, + target: viser.ViserServer | viser.ClientHandle, + urdf_or_path: yourdfpy.URDF | Path, + scale: float = 1.0, + root_node_name: str = "/", + mesh_color_override: tuple[float, float, float] | None = None, + ) -> None: + assert root_node_name.startswith("/") + assert len(root_node_name) == 1 or not root_node_name.endswith("/") + + if isinstance(urdf_or_path, Path): + urdf = yourdfpy.URDF.load( + urdf_or_path, + filename_handler=partial( + yourdfpy.filename_handler_magic, dir=urdf_or_path.parent + ), + ) + else: + urdf = urdf_or_path + assert isinstance(urdf, yourdfpy.URDF) + + self._target = target + self._urdf = urdf + self._scale = scale + self._root_node_name = root_node_name + + # Add coordinate frame for each joint. + self._joint_frames: List[viser.SceneNodeHandle] = [] + for joint in self._urdf.joint_map.values(): + assert isinstance(joint, yourdfpy.Joint) + self._joint_frames.append( + self._target.scene.add_frame( + _viser_name_from_frame( + self._urdf, joint.child, self._root_node_name + ), + show_axes=False, + ) + ) + + # Add the URDF's meshes/geometry to viser. + for link_name, mesh in urdf.scene.geometry.items(): + assert isinstance(mesh, trimesh.Trimesh) + T_parent_child = urdf.get_transform( + link_name, urdf.scene.graph.transforms.parents[link_name] + ) + name = _viser_name_from_frame(urdf, link_name, root_node_name) + + # Scale + transform the mesh. (these will mutate it!) + # + # It's important that we use apply_transform() instead of unpacking + # the rotation/translation terms, since the scene graph transform + # can also contain scale and reflection terms. + mesh = mesh.copy() + mesh.apply_scale(self._scale) + mesh.apply_transform(T_parent_child) + + if mesh_color_override is None: + target.scene.add_mesh_trimesh(name, mesh) + else: + target.scene.add_mesh_simple( + name, + mesh.vertices, + mesh.faces, + color=mesh_color_override, + ) + +
+[docs] + def update_cfg(self, configuration: onp.ndarray) -> None: + """Update the joint angles of the visualized URDF.""" + self._urdf.update_cfg(configuration) + with self._target.atomic(): + for joint, frame_handle in zip( + self._urdf.joint_map.values(), self._joint_frames + ): + assert isinstance(joint, yourdfpy.Joint) + T_parent_child = self._urdf.get_transform(joint.child, joint.parent) + frame_handle.wxyz = tf.SO3.from_matrix(T_parent_child[:3, :3]).wxyz + frame_handle.position = T_parent_child[:3, 3] * self._scale
+ + +
+[docs] + def get_actuated_joint_limits( + self, + ) -> dict[str, tuple[float | None, float | None]]: + """Returns an ordered mapping from actuated joint names to position limits.""" + out: dict[str, tuple[float | None, float | None]] = {} + for joint_name, joint in zip( + self._urdf.actuated_joint_names, self._urdf.actuated_joints + ): + assert isinstance(joint_name, str) + assert isinstance(joint, yourdfpy.Joint) + if joint.limit is None: + out[joint_name] = (-onp.pi, onp.pi) + else: + out[joint_name] = (joint.limit.lower, joint.limit.upper) + return out
+ + +
+[docs] + def get_actuated_joint_names(self) -> Tuple[str, ...]: + """Returns a tuple of actuated joint names, in order.""" + return tuple(self._urdf.actuated_joint_names)
+
+ + + +def _viser_name_from_frame( + urdf: yourdfpy.URDF, + frame_name: str, + root_node_name: str = "/", +) -> str: + """Given the (unique) name of a frame in our URDF's kinematic tree, return a + scene node name for viser. + + For a robot manipulator with four frames, that looks like: + + + ((shoulder)) == ((elbow)) + / / |X| + / / ((wrist)) + ____/ /____ |X| + [ ] [=======] + [ base_link ] [] [] + [___________] + + + this would map a name like "elbow" to "base_link/shoulder/elbow". + """ + assert root_node_name.startswith("/") + assert len(root_node_name) == 1 or not root_node_name.endswith("/") + + frames = [] + while frame_name != urdf.scene.graph.base_frame: + frames.append(frame_name) + frame_name = urdf.scene.graph.transforms.parents[frame_name] + if root_node_name != "/": + frames.append(root_node_name) + return "/".join(frames[::-1]) +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/infra/_infra/index.html b/versions/0.2.2/_modules/viser/infra/_infra/index.html new file mode 100644 index 000000000..e39c31900 --- /dev/null +++ b/versions/0.2.2/_modules/viser/infra/_infra/index.html @@ -0,0 +1,898 @@ + + + + + + + + + + + + viser.infra._infra - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser.infra._infra

+from __future__ import annotations
+
+import abc
+import asyncio
+import contextlib
+import dataclasses
+import http
+import mimetypes
+import queue
+import threading
+from asyncio.events import AbstractEventLoop
+from concurrent.futures import ThreadPoolExecutor
+from pathlib import Path
+from typing import (
+    Any,
+    Callable,
+    Dict,
+    Generator,
+    List,
+    NewType,
+    Optional,
+    Tuple,
+    Type,
+    TypeVar,
+)
+
+import msgpack
+import rich
+import websockets.connection
+import websockets.datastructures
+import websockets.exceptions
+import websockets.server
+from typing_extensions import Literal, assert_never, override
+from websockets.legacy.server import WebSocketServerProtocol
+
+from ._async_message_buffer import AsyncMessageBuffer
+from ._messages import Message
+
+
+@dataclasses.dataclass
+class _ClientHandleState:
+    # Internal state for ClientConnection objects.
+    # message_buffer: asyncio.Queue
+    message_buffer: AsyncMessageBuffer
+    event_loop: AbstractEventLoop
+
+
+ClientId = NewType("ClientId", int)
+TMessage = TypeVar("TMessage", bound=Message)
+
+
+
+[docs] +class WebsockMessageHandler: + """Mix-in for adding message handling to a class.""" + + def __init__(self, thread_executor: ThreadPoolExecutor) -> None: + self._thread_executor = thread_executor + self._incoming_handlers: Dict[ + Type[Message], List[Callable[[ClientId, Message], None]] + ] = {} + self._atomic_lock = threading.Lock() + self._queued_messages: queue.Queue = queue.Queue() + self._locked_thread_id = -1 + +
+[docs] + def register_handler( + self, + message_cls: Type[TMessage], + callback: Callable[[ClientId, TMessage], Any], + ) -> None: + """Register a handler for a particular message type.""" + if message_cls not in self._incoming_handlers: + self._incoming_handlers[message_cls] = [] + self._incoming_handlers[message_cls].append(callback) # type: ignore
+ + +
+[docs] + def unregister_handler( + self, + message_cls: Type[TMessage], + callback: Optional[Callable[[ClientId, TMessage], Any]] = None, + ): + """Unregister a handler for a particular message type.""" + assert ( + message_cls in self._incoming_handlers + ), "Tried to unregister a handler that hasn't been registered." + if callback is None: + self._incoming_handlers.pop(message_cls) + else: + self._incoming_handlers[message_cls].remove(callback) # type: ignore
+ + + def _handle_incoming_message(self, client_id: ClientId, message: Message) -> None: + """Handle incoming messages.""" + if type(message) in self._incoming_handlers: + for cb in self._incoming_handlers[type(message)]: + cb(client_id, message) + +
+[docs] + @abc.abstractmethod + def unsafe_send_message(self, message: Message) -> None: ...
+ + +
+[docs] + def queue_message(self, message: Message) -> None: + """Wrapped method for sending messages safely.""" + got_lock = self._atomic_lock.acquire(blocking=False) + if got_lock: + self.unsafe_send_message(message) + self._atomic_lock.release() + else: + # Send when lock is acquirable, while retaining message order. + # This could be optimized! + self._queued_messages.put(message) + + def try_again() -> None: + with self._atomic_lock: + self.unsafe_send_message(self._queued_messages.get()) + + self._thread_executor.submit(try_again)
+ + +
+[docs] + @contextlib.contextmanager + def atomic(self) -> Generator[None, None, None]: + """Returns a context where: all outgoing messages are grouped and applied by + clients atomically. + + This should be treated as a soft constraint that's helpful for things + like animations, or when we want position and orientation updates to + happen synchronously. + + Returns: + Context manager. + """ + # If called multiple times in the same thread, we ignore inner calls. + thread_id = threading.get_ident() + if thread_id == self._locked_thread_id: + got_lock = False + else: + self._atomic_lock.acquire() + self._locked_thread_id = thread_id + got_lock = True + + yield + + if got_lock: + self._atomic_lock.release() + self._locked_thread_id = -1
+
+ + + +
+[docs] +class WebsockClientConnection(WebsockMessageHandler): + """Handle for sending messages to and listening to messages from a single + connected client.""" + + def __init__( + self, + client_id: int, + thread_executor: ThreadPoolExecutor, + client_state: _ClientHandleState, + ) -> None: + self.client_id = client_id + self._state = client_state + super().__init__(thread_executor) + +
+[docs] + @override + def unsafe_send_message(self, message: Message) -> None: + """Send a message to a specific client.""" + self._state.message_buffer.push(message)
+
+ + + +
+[docs] +class WebsockServer(WebsockMessageHandler): + """Websocket server abstraction. Communicates asynchronously with client + applications. + + By default, all messages are broadcasted to all connected clients. + + To send messages to an individual client, we can use `on_client_connect()` to + retrieve client handles. + + Args: + host: Host to bind server to. + port: Port to bind server to. + message_class: Base class for message types. Subclasses of the message type + should have unique names. This argument is optional currently, but will be + required in the future. + http_server_root: Path to root for HTTP server. + verbose: Toggle for print messages. + client_api_version: Flag for backwards compatibility. 0 sends individual + messages. 1 sends windowed messages. + """ + + def __init__( + self, + host: str, + port: int, + message_class: Type[Message] = Message, + http_server_root: Optional[Path] = None, + verbose: bool = True, + client_api_version: Literal[0, 1] = 0, + ): + super().__init__(thread_executor=ThreadPoolExecutor(max_workers=32)) + + # Track connected clients. + self._client_connect_cb: List[Callable[[WebsockClientConnection], None]] = [] + self._client_disconnect_cb: List[Callable[[WebsockClientConnection], None]] = [] + + self._host = host + self._port = port + self._message_class = message_class + self._http_server_root = http_server_root + self._verbose = verbose + self._client_api_version: Literal[0, 1] = client_api_version + self._shutdown_event = threading.Event() + + self._client_state_from_id: Dict[int, _ClientHandleState] = {} + +
+[docs] + def start(self) -> None: + """Start the server.""" + + # Start server thread. + ready_sem = threading.Semaphore(value=1) + ready_sem.acquire() + threading.Thread( + target=lambda: self._background_worker(ready_sem), + daemon=True, + ).start() + + # Wait for the thread to set self._event_loop and self._broadcast_buffer... + ready_sem.acquire() + + # Broadcast buffer should be populated by the background worker. + assert isinstance(self._broadcast_buffer, AsyncMessageBuffer)
+ + +
+[docs] + def stop(self) -> None: + """Stop the server.""" + self._thread_executor.shutdown(wait=True) + self._event_loop.stop()
+ + +
+[docs] + def on_client_connect(self, cb: Callable[[WebsockClientConnection], Any]) -> None: + """Attach a callback to run for newly connected clients.""" + self._client_connect_cb.append(cb)
+ + +
+[docs] + def on_client_disconnect( + self, cb: Callable[[WebsockClientConnection], Any] + ) -> None: + """Attach a callback to run when clients disconnect.""" + self._client_disconnect_cb.append(cb)
+ + +
+[docs] + @override + def unsafe_send_message(self, message: Message) -> None: + """Pushes a message onto the broadcast queue. Message will be sent to all clients. + + Broadcasted messages are persistent: if a new client connects to the server, + they will receive a buffered set of previously broadcasted messages. The buffer + is culled using the value of `message.redundancy_key()`.""" + self._broadcast_buffer.push(message)
+ + +
+[docs] + def flush(self) -> None: + """Flush the outgoing message buffer for broadcasted messages. Any buffered + messages will immediately be sent. (by default they are windowed)""" + # TODO: we should add a flush event. + self._broadcast_buffer.flush()
+ + +
+[docs] + def flush_client(self, client_id: int) -> None: + """Flush the outgoing message buffer for a particular client. Any buffered + messages will immediately be sent. (by default they are windowed)""" + self._client_state_from_id[client_id].message_buffer.flush()
+ + + def _background_worker(self, ready_sem: threading.Semaphore) -> None: + host = self._host + port = self._port + message_class = self._message_class + http_server_root = self._http_server_root + + # Need to make a new event loop for notebook compatbility. + event_loop = asyncio.new_event_loop() + asyncio.set_event_loop(event_loop) + self._event_loop = event_loop + self._broadcast_buffer = AsyncMessageBuffer( + event_loop, persistent_messages=True + ) + + count_lock = asyncio.Lock() + connection_count = 0 + total_connections = 0 + + async def serve(websocket: WebSocketServerProtocol) -> None: + """Server loop, run once per connection.""" + + async with count_lock: + nonlocal connection_count + client_id = ClientId(connection_count) + connection_count += 1 + + nonlocal total_connections + total_connections += 1 + + if self._verbose: + rich.print( + f"[bold](viser)[/bold] Connection opened ({client_id}," + f" {total_connections} total)," + f" {len(self._broadcast_buffer.message_from_id)} persistent" + " messages" + ) + + client_state = _ClientHandleState( + AsyncMessageBuffer(event_loop, persistent_messages=False), + event_loop, + ) + client_connection = WebsockClientConnection( + client_id, self._thread_executor, client_state + ) + self._client_state_from_id[client_id] = client_state + + def handle_incoming(message: Message) -> None: + self._thread_executor.submit( + error_print_wrapper( + lambda: self._handle_incoming_message(client_id, message) + ) + ) + self._thread_executor.submit( + error_print_wrapper( + lambda: client_connection._handle_incoming_message( + client_id, message + ) + ) + ) + + # New connection callbacks. + for cb in self._client_connect_cb: + cb(client_connection) + + try: + # For each client: infinite loop over producers (which send messages) + # and consumers (which receive messages). + await asyncio.gather( + _message_producer( + websocket, + client_state.message_buffer, + client_id, + self._client_api_version, + ), + _message_producer( + websocket, + self._broadcast_buffer, + client_id, + self._client_api_version, + ), + _message_consumer(websocket, handle_incoming, message_class), + ) + except ( + websockets.exceptions.ConnectionClosedOK, + websockets.exceptions.ConnectionClosedError, + ): + # We use a sentinel value to signal that the client producer thread + # should exit. + # + # This is partially cosmetic: it allows us to safely finish pending + # queue get() tasks, which suppresses a "Task was destroyed but it is + # pending" error. + client_state.message_buffer.set_done() + + # Disconnection callbacks. + for cb in self._client_disconnect_cb: + cb(client_connection) + + # Cleanup. + self._client_state_from_id.pop(client_id) + total_connections -= 1 + if self._verbose: + rich.print( + f"[bold](viser)[/bold] Connection closed ({client_id}," + f" {total_connections} total)" + ) + + # Host client on the same port as the websocket. + file_cache: Dict[Path, bytes] = {} + file_cache_gzipped: Dict[Path, bytes] = {} + + import gzip + + async def viser_http_server( + path: str, request_headers: websockets.datastructures.Headers + ) -> Optional[ + Tuple[http.HTTPStatus, websockets.datastructures.HeadersLike, bytes] + ]: + # Ignore websocket packets. + if request_headers.get("Upgrade") == "websocket": + return None + + # Strip out search params, get relative path. + path = path.partition("?")[0] + relpath = str(Path(path).relative_to("/")) + if relpath == ".": + relpath = "index.html" + assert http_server_root is not None + + source_path = http_server_root / relpath + if not source_path.exists(): + return (http.HTTPStatus.NOT_FOUND, {}, b"404") # type: ignore + + use_gzip = "gzip" in request_headers.get("Accept-Encoding", "") + + mime_type = mimetypes.guess_type(relpath)[0] + if mime_type is None: + mime_type = "application/octet-stream" + response_headers = { + "Content-Type": mime_type, + } + + if source_path not in file_cache: + file_cache[source_path] = source_path.read_bytes() + if use_gzip: + response_headers["Content-Encoding"] = "gzip" + if source_path not in file_cache_gzipped: + file_cache_gzipped[source_path] = gzip.compress( + file_cache[source_path] + ) + response_payload = file_cache_gzipped[source_path] + else: + response_payload = file_cache[source_path] + + # Try to read + send over file. + return (http.HTTPStatus.OK, response_headers, response_payload) + + for _ in range(500): + try: + event_loop.run_until_complete( + websockets.server.serve( + serve, + host, + port, + compression=None, + process_request=( + viser_http_server if http_server_root is not None else None + ), + ) + ) + break + except OSError: # Port not available. + port += 1 + continue + + self._port = port + ready_sem.release() + event_loop.run_forever() + rich.print("[bold](viser)[/bold] Server stopped")
+ + + +async def _message_producer( + websocket: WebSocketServerProtocol, + buffer: AsyncMessageBuffer, + client_id: int, + client_api_version: Literal[0, 1], +) -> None: + """Infinite loop to broadcast windows of messages from a buffer.""" + window_generator = buffer.window_generator(client_id) + while not buffer.done: + outgoing = await window_generator.__anext__() + if client_api_version == 1: + serialized = msgpack.packb( + tuple(message.as_serializable_dict() for message in outgoing) + ) + assert isinstance(serialized, bytes) + await websocket.send(serialized) + elif client_api_version == 0: + for msg in outgoing: + serialized = msgpack.packb(msg.as_serializable_dict()) + assert isinstance(serialized, bytes) + await websocket.send(serialized) + else: + assert_never(client_api_version) + + +async def _message_consumer( + websocket: WebSocketServerProtocol, + handle_message: Callable[[Message], None], + message_class: Type[Message], +) -> None: + """Infinite loop waiting for and then handling incoming messages.""" + while True: + raw = await websocket.recv() + assert isinstance(raw, bytes) + message = message_class.deserialize(raw) + handle_message(message) + + +def error_print_wrapper(inner: Callable[[], Any]) -> Callable[[], None]: + """Wrap a Callable to print error messages when they happen. + + This can be helpful for jobs submitted to ThreadPoolExecutor instances, which, by + default, will suppress error messages until returned futures are awaited. + """ + + def wrapped() -> None: + try: + inner() + except Exception as e: + import traceback as tb + + tb.print_exception(type(e), e, e.__traceback__, limit=100) + + return wrapped +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/infra/_messages/index.html b/versions/0.2.2/_modules/viser/infra/_messages/index.html new file mode 100644 index 000000000..7546600c1 --- /dev/null +++ b/versions/0.2.2/_modules/viser/infra/_messages/index.html @@ -0,0 +1,538 @@ + + + + + + + + + + + + viser.infra._messages - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser.infra._messages

+"""Message type definitions. For synchronization with the TypeScript definitions, see
+`_typescript_interface_gen.py.`"""
+
+from __future__ import annotations
+
+import abc
+import functools
+import warnings
+from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, TypeVar, cast
+
+import msgpack
+import numpy as onp
+from typing_extensions import get_args, get_origin, get_type_hints
+
+if TYPE_CHECKING:
+    from ._infra import ClientId
+else:
+    ClientId = Any
+
+
+def _prepare_for_deserialization(value: Any, annotation: Type) -> Any:
+    # If annotated as a float but we got an integer, cast to float. These
+    # are both `number` in Javascript.
+    if annotation is float:
+        return float(value)
+    elif annotation is int:
+        return int(value)
+    elif get_origin(annotation) is tuple:
+        out = []
+        args = get_args(annotation)
+        if len(args) >= 2 and args[1] == ...:
+            args = (args[0],) * len(value)
+        elif len(value) != len(args):
+            warnings.warn(f"[viser] {value} does not match annotation {annotation}")
+            return value
+
+        for i, v in enumerate(value):
+            out.append(
+                # Hack to be OK with wrong type annotations.
+                # https://github.com/nerfstudio-project/nerfstudio/pull/1805
+                _prepare_for_deserialization(v, args[i]) if i < len(args) else v
+            )
+        return tuple(out)
+    return value
+
+
+def _prepare_for_serialization(value: Any, annotation: Type) -> Any:
+    """Prepare any special types for serialization."""
+    if annotation is Any:
+        annotation = type(value)
+
+    # Coerce some scalar types: if we've annotated as float / int but we get an
+    # onp.float32 / onp.int64, for example, we should cast automatically.
+    if annotation is float:
+        return float(value)
+    if annotation is int:
+        return int(value)
+
+    # Recursively handle tuples.
+    if get_origin(annotation) is tuple:
+        if isinstance(value, onp.ndarray):
+            assert False, (
+                "Expected a tuple, but got an array... missing a cast somewhere?"
+                f" {value}"
+            )
+
+        out = []
+        args = get_args(annotation)
+        if len(args) >= 2 and args[1] == ...:
+            args = (args[0],) * len(value)
+        elif len(value) != len(args):
+            warnings.warn(f"[viser] {value} does not match annotation {annotation}")
+            return value
+
+        for i, v in enumerate(value):
+            out.append(
+                # Hack to be OK with wrong type annotations.
+                # https://github.com/nerfstudio-project/nerfstudio/pull/1805
+                _prepare_for_serialization(v, args[i]) if i < len(args) else v
+            )
+        return tuple(out)
+
+    # For arrays, we serialize underlying data directly. The client is responsible for
+    # reading using the correct dtype.
+    if isinstance(value, onp.ndarray):
+        return value.data if value.data.c_contiguous else value.copy().data
+
+    return value
+
+
+T = TypeVar("T", bound="Message")
+
+
+@functools.lru_cache(maxsize=None)
+def get_type_hints_cached(cls: Type[Any]) -> Dict[str, Any]:
+    return get_type_hints(cls)  # type: ignore
+
+
+
+[docs] +class Message(abc.ABC): + """Base message type for server/client communication.""" + + excluded_self_client: Optional[ClientId] = None + """Don't send this message to a particular client. Useful when a client wants to + send synchronization information to other clients.""" + +
+[docs] + def as_serializable_dict(self) -> Dict[str, Any]: + """Convert a Python Message object into bytes.""" + message_type = type(self) + hints = get_type_hints_cached(message_type) + out = { + k: _prepare_for_serialization(v, hints[k]) for k, v in vars(self).items() + } + out["type"] = message_type.__name__ + return out
+ + + @classmethod + def _from_serializable_dict(cls, mapping: Dict[str, Any]) -> Dict[str, Any]: + """Convert a dict message back into a Python Message object.""" + + hints = get_type_hints_cached(cls) + + mapping = { + k: _prepare_for_deserialization(v, hints[k]) for k, v in mapping.items() + } + return mapping + +
+[docs] + @classmethod + def deserialize(cls, message: bytes) -> Message: + """Convert bytes into a Python Message object.""" + mapping = msgpack.unpackb(message) + + # msgpack deserializes to lists by default, but all of our annotations use + # tuples. + mapping = { + k: tuple(v) if isinstance(v, list) else v for k, v in mapping.items() + } + message_type = cls._subclass_from_type_string()[cast(str, mapping.pop("type"))] + message_kwargs = message_type._from_serializable_dict(mapping) + return message_type(**message_kwargs)
+ + + @classmethod + @functools.lru_cache(maxsize=100) + def _subclass_from_type_string(cls: Type[T]) -> Dict[str, Type[T]]: + subclasses = cls.get_subclasses() + return {s.__name__: s for s in subclasses} + +
+[docs] + @classmethod + def get_subclasses(cls: Type[T]) -> List[Type[T]]: + """Recursively get message subclasses.""" + + def _get_subclasses(typ: Type[T]) -> List[Type[T]]: + out = [] + for sub in typ.__subclasses__(): + out.append(sub) + out.extend(_get_subclasses(sub)) + return out + + return _get_subclasses(cls)
+ + +
+[docs] + @abc.abstractmethod + def redundancy_key(self) -> str: + """Returns a unique key for this message, used for detecting redundant + messages. + + For example: if we send 1000 "set value" messages for the same GUI element, we + should only keep the latest message. + """
+
+ +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/infra/_typescript_interface_gen/index.html b/versions/0.2.2/_modules/viser/infra/_typescript_interface_gen/index.html new file mode 100644 index 000000000..9d33e895a --- /dev/null +++ b/versions/0.2.2/_modules/viser/infra/_typescript_interface_gen/index.html @@ -0,0 +1,538 @@ + + + + + + + + + + + + viser.infra._typescript_interface_gen - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser.infra._typescript_interface_gen

+import dataclasses
+from collections import defaultdict
+from typing import Any, Type, Union, cast
+
+import numpy as onp
+from typing_extensions import (
+    Annotated,
+    Literal,
+    NotRequired,
+    get_args,
+    get_origin,
+    get_type_hints,
+    is_typeddict,
+)
+
+try:
+    from typing import Literal as LiteralAlt
+except ImportError:
+    LiteralAlt = Literal  # type: ignore
+
+from ._messages import Message
+
+_raw_type_mapping = {
+    bool: "boolean",
+    float: "number",
+    int: "number",
+    str: "string",
+    # For numpy arrays, we directly serialize the underlying data buffer.
+    onp.ndarray: "Uint8Array",
+    bytes: "Uint8Array",
+    Any: "any",
+    None: "null",
+    type(None): "null",
+}
+
+
+def _get_ts_type(typ: Type[Any]) -> str:
+    origin_typ = get_origin(typ)
+
+    # Look for TypeScriptAnnotationOverride in the annotations.
+    if origin_typ is Annotated:
+        args = get_args(typ)
+        for arg in args[1:]:
+            if isinstance(arg, TypeScriptAnnotationOverride):
+                return arg.annotation
+
+        # If no override is found, just use the unwrapped type.
+        origin_typ = args[0]
+
+    # Automatic Python => TypeScript conversion.
+    if origin_typ is tuple:
+        args = get_args(typ)
+        if len(args) == 2 and args[1] == ...:
+            return _get_ts_type(args[0]) + "[]"
+        else:
+            return "[" + ", ".join(map(_get_ts_type, args)) + "]"
+    elif origin_typ is list:
+        args = get_args(typ)
+        assert len(args) == 1
+        return _get_ts_type(args[0]) + "[]"
+    elif origin_typ in (Literal, LiteralAlt):
+        return " | ".join(
+            map(
+                lambda lit: repr(lit).lower() if type(lit) is bool else repr(lit),
+                get_args(typ),
+            )
+        )
+    elif origin_typ is Union:
+        return (
+            "("
+            + " | ".join(
+                map(
+                    _get_ts_type,
+                    get_args(typ),
+                )
+            )
+            + ")"
+        )
+    elif origin_typ is list:
+        args = get_args(typ)
+        return _get_ts_type(args[0]) + "[]"
+    elif origin_typ is dict:
+        args = get_args(typ)
+        assert len(args) == 2
+        return "{ [key: " + _get_ts_type(args[0]) + "]: " + _get_ts_type(args[1]) + " }"
+    elif is_typeddict(typ):
+        hints = get_type_hints(typ)
+        optional_keys = getattr(typ, "__optional_keys__", [])
+
+        def fmt(key):
+            val = hints[key]
+            optional = key in optional_keys
+            if get_origin(val) is NotRequired:
+                val = get_args(val)[0]
+            ret = f"'{key}'{'?' if optional else ''}" + ": " + _get_ts_type(val)
+            return ret
+
+        ret = "{" + ", ".join(map(fmt, hints)) + "}"
+        return ret
+    else:
+        # Like get_origin(), but also supports numpy.typing.NDArray[dtype].
+        typ = cast(Any, getattr(typ, "__origin__", typ))
+
+        assert typ in _raw_type_mapping, f"Unsupported type {typ}"
+        return _raw_type_mapping[typ]
+
+
+
+[docs] +@dataclasses.dataclass(frozen=True) +class TypeScriptAnnotationOverride: + """Use with `typing.Annotated[]` to override the automatically-generated + TypeScript annotation corresponding to a dataclass field.""" + + annotation: str
+ + + +
+[docs] +def generate_typescript_interfaces(message_cls: Type[Message]) -> str: + """Generate TypeScript definitions for all subclasses of a base message class.""" + out_lines = [] + message_types = message_cls.get_subclasses() + tag_map = defaultdict(list) + + # Generate interfaces for each specific message. + for cls in message_types: + if cls.__doc__ is not None: + docstring = "\n * ".join( + map(lambda line: line.strip(), cls.__doc__.split("\n")) + ) + out_lines.append(f"/** {docstring}") + out_lines.append(" *") + out_lines.append(" * (automatically generated)") + out_lines.append(" */") + + for tag in getattr(cls, "_tags", []): + tag_map[tag].append(cls.__name__) + + out_lines.append(f"export interface {cls.__name__} " + "{") + out_lines.append(f' type: "{cls.__name__}";') + field_names = set([f.name for f in dataclasses.fields(cls)]) # type: ignore + for name, typ in get_type_hints(cls, include_extras=True).items(): + if name in field_names: + typ = _get_ts_type(typ) + else: + continue + out_lines.append(f" {name}: {typ};") + out_lines.append("}") + out_lines.append("") + + # Generate union type over all messages. + out_lines.append("export type Message = ") + for cls in message_types: + out_lines.append(f" | {cls.__name__}") + out_lines[-1] = out_lines[-1] + ";" + + # Generate union type over all tags. + for tag, cls_names in tag_map.items(): + out_lines.append(f"export type {tag} = ") + for cls_name in cls_names: + out_lines.append(f" | {cls_name}") + out_lines[-1] = out_lines[-1] + ";" + + interfaces = "\n".join(out_lines) + "\n" + + # Add header and return. + return ( + "\n".join( + [ + ( + "// AUTOMATICALLY GENERATED message interfaces, from Python" + " dataclass definitions." + ), + "// This file should not be manually modified.", + "", + ] + ) + + interfaces + )
+ +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/transforms/_base/index.html b/versions/0.2.2/_modules/viser/transforms/_base/index.html new file mode 100644 index 000000000..4e7b3adbe --- /dev/null +++ b/versions/0.2.2/_modules/viser/transforms/_base/index.html @@ -0,0 +1,721 @@ + + + + + + + + + + + + viser.transforms._base - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser.transforms._base

+import abc
+from typing import ClassVar, Generic, Tuple, TypeVar, Union, overload
+
+import numpy as onp
+import numpy.typing as onpt
+from typing_extensions import Self, final, get_args, override
+
+
+
+[docs] +class MatrixLieGroup(abc.ABC): + """Interface definition for matrix Lie groups.""" + + # Class properties. + # > These will be set in `_utils.register_lie_group()`. + + matrix_dim: ClassVar[int] + """Dimension of square matrix output from `.as_matrix()`.""" + + parameters_dim: ClassVar[int] + """Dimension of underlying parameters, `.parameters()`.""" + + tangent_dim: ClassVar[int] + """Dimension of tangent space.""" + + space_dim: ClassVar[int] + """Dimension of coordinates that can be transformed.""" + + def __init__( + # Notes: + # - For the constructor signature to be consistent with subclasses, `parameters` + # should be marked as positional-only. But this isn't possible in Python 3.7. + # - This method is implicitly overriden by the dataclass decorator and + # should _not_ be marked abstract. + self, + parameters: onp.ndarray, + ): + """Construct a group object from its underlying parameters.""" + raise NotImplementedError() + + # Shared implementations. + + @overload + def __matmul__(self, other: Self) -> Self: ... + + @overload + def __matmul__( + self, other: onpt.NDArray[onp.floating] + ) -> onpt.NDArray[onp.floating]: ... + +
+[docs] + def __matmul__( + self, other: Union[Self, onpt.NDArray[onp.floating]] + ) -> Union[Self, onpt.NDArray[onp.floating]]: + """Overload for the `@` operator. + + Switches between the group action (`.apply()`) and multiplication + (`.multiply()`) based on the type of `other`. + """ + if isinstance(other, onp.ndarray): + return self.apply(target=other) + elif isinstance(other, MatrixLieGroup): + assert self.space_dim == other.space_dim + return self.multiply(other=other) + else: + assert False, f"Invalid argument type for `@` operator: {type(other)}"
+ + + # Factory. + +
+[docs] + @classmethod + @abc.abstractmethod + def identity(cls, batch_axes: Tuple[int, ...] = ()) -> Self: + """Returns identity element. + + Args: + batch_axes: Any leading batch axes for the output transform. + + Returns: + Identity element. + """
+ + +
+[docs] + @classmethod + @abc.abstractmethod + def from_matrix(cls, matrix: onpt.NDArray[onp.floating]) -> Self: + """Get group member from matrix representation. + + Args: + matrix: Matrix representaiton. + + Returns: + Group member. + """
+ + + # Accessors. + +
+[docs] + @abc.abstractmethod + def as_matrix(self) -> onpt.NDArray[onp.floating]: + """Get transformation as a matrix. Homogeneous for SE groups."""
+ + +
+[docs] + @abc.abstractmethod + def parameters(self) -> onpt.NDArray[onp.floating]: + """Get underlying representation."""
+ + + # Operations. + +
+[docs] + @abc.abstractmethod + def apply(self, target: onpt.NDArray[onp.floating]) -> onpt.NDArray[onp.floating]: + """Applies group action to a point. + + Args: + target: Point to transform. + + Returns: + Transformed point. + """
+ + +
+[docs] + @abc.abstractmethod + def multiply(self, other: Self) -> Self: + """Composes this transformation with another. + + Returns: + self @ other + """
+ + +
+[docs] + @classmethod + @abc.abstractmethod + def exp(cls, tangent: onpt.NDArray[onp.floating]) -> Self: + """Computes `expm(wedge(tangent))`. + + Args: + tangent: Tangent vector to take the exponential of. + + Returns: + Output. + """
+ + +
+[docs] + @abc.abstractmethod + def log(self) -> onpt.NDArray[onp.floating]: + """Computes `vee(logm(transformation matrix))`. + + Returns: + Output. Shape should be `(tangent_dim,)`. + """
+ + +
+[docs] + @abc.abstractmethod + def adjoint(self) -> onpt.NDArray[onp.floating]: + """Computes the adjoint, which transforms tangent vectors between tangent + spaces. + + More precisely, for a transform `GroupType`: + ``` + GroupType @ exp(omega) = exp(Adj_T @ omega) @ GroupType + ``` + + In robotics, typically used for transforming twists, wrenches, and Jacobians + across different reference frames. + + Returns: + Output. Shape should be `(tangent_dim, tangent_dim)`. + """
+ + +
+[docs] + @abc.abstractmethod + def inverse(self) -> Self: + """Computes the inverse of our transform. + + Returns: + Output. + """
+ + +
+[docs] + @abc.abstractmethod + def normalize(self) -> Self: + """Normalize/projects values and returns. + + Returns: + Normalized group member. + """
+ + + # @classmethod + # @abc.abstractmethod + # def sample_uniform(cls, key: onp.ndarray, batch_axes: Tuple[int, ...] = ()) -> Self: + # """Draw a uniform sample from the group. Translations (if applicable) are in the + # range [-1, 1]. + # + # Args: + # key: PRNG key, as returned by `jax.random.PRNGKey()`. + # batch_axes: Any leading batch axes for the output transforms. Each + # sampled transform will be different. + # + # Returns: + # Sampled group member. + # """ + +
+[docs] + @final + def get_batch_axes(self) -> Tuple[int, ...]: + """Return any leading batch axes in contained parameters. If an array of shape + `(100, 4)` is placed in the wxyz field of an SO3 object, for example, this will + return `(100,)`.""" + return self.parameters().shape[:-1]
+
+ + + +
+[docs] +class SOBase(MatrixLieGroup): + """Base class for special orthogonal groups."""
+ + + +ContainedSOType = TypeVar("ContainedSOType", bound=SOBase) + + +
+[docs] +class SEBase(Generic[ContainedSOType], MatrixLieGroup): + """Base class for special Euclidean groups. + + Each SE(N) group member contains an SO(N) rotation, as well as an N-dimensional + translation vector. + """ + + # SE-specific interface. + +
+[docs] + @classmethod + @abc.abstractmethod + def from_rotation_and_translation( + cls, + rotation: ContainedSOType, + translation: onpt.NDArray[onp.floating], + ) -> Self: + """Construct a rigid transform from a rotation and a translation. + + Args: + rotation: Rotation term. + translation: translation term. + + Returns: + Constructed transformation. + """
+ + +
+[docs] + @final + @classmethod + def from_rotation(cls, rotation: ContainedSOType) -> Self: + return cls.from_rotation_and_translation( + rotation=rotation, + translation=onp.zeros( + (*rotation.get_batch_axes(), cls.space_dim), + dtype=rotation.parameters().dtype, + ), + )
+ + +
+[docs] + @final + @classmethod + def from_translation(cls, translation: onpt.NDArray[onp.floating]) -> Self: + # Extract rotation class from type parameter. + assert len(cls.__orig_bases__) == 1 # type: ignore + return cls.from_rotation_and_translation( + rotation=get_args(cls.__orig_bases__[0])[0].identity(), # type: ignore + translation=translation, + )
+ + +
+[docs] + @abc.abstractmethod + def rotation(self) -> ContainedSOType: + """Returns a transform's rotation term."""
+ + +
+[docs] + @abc.abstractmethod + def translation(self) -> onpt.NDArray[onp.floating]: + """Returns a transform's translation term."""
+ + + # Overrides. + +
+[docs] + @final + @override + def apply(self, target: onpt.NDArray[onp.floating]) -> onpt.NDArray[onp.floating]: + return self.rotation() @ target + self.translation() # type: ignore
+ + +
+[docs] + @final + @override + def multiply(self, other: Self) -> Self: + return type(self).from_rotation_and_translation( + rotation=self.rotation() @ other.rotation(), + translation=(self.rotation() @ other.translation()) + self.translation(), + )
+ + +
+[docs] + @final + @override + def inverse(self) -> Self: + R_inv = self.rotation().inverse() + return type(self).from_rotation_and_translation( + rotation=R_inv, + translation=-(R_inv @ self.translation()), + )
+ + +
+[docs] + @final + @override + def normalize(self) -> Self: + return type(self).from_rotation_and_translation( + rotation=self.rotation().normalize(), + translation=self.translation(), + )
+
+ +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/transforms/_se2/index.html b/versions/0.2.2/_modules/viser/transforms/_se2/index.html new file mode 100644 index 000000000..e310c54de --- /dev/null +++ b/versions/0.2.2/_modules/viser/transforms/_se2/index.html @@ -0,0 +1,656 @@ + + + + + + + + + + + + viser.transforms._se2 - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser.transforms._se2

+import dataclasses
+from typing import Tuple, cast
+
+import numpy as onp
+import numpy.typing as onpt
+from typing_extensions import override
+
+from . import _base, hints
+from ._so2 import SO2
+from .utils import broadcast_leading_axes, get_epsilon, register_lie_group
+
+
+
+[docs] +@register_lie_group( + matrix_dim=3, + parameters_dim=4, + tangent_dim=3, + space_dim=2, +) +@dataclasses.dataclass(frozen=True) +class SE2(_base.SEBase[SO2]): + """Special Euclidean group for proper rigid transforms in 2D. Broadcasting + rules are the same as for numpy. + + Ported to numpy from `jaxlie.SE2`. + + Internal parameterization is `(cos, sin, x, y)`. Tangent parameterization is `(vx, + vy, omega)`. + """ + + # SE2-specific. + + unit_complex_xy: onpt.NDArray[onp.floating] + """Internal parameters. `(cos, sin, x, y)`. Shape should be `(*, 4)`.""" + + @override + def __repr__(self) -> str: + unit_complex = onp.round(self.unit_complex_xy[..., :2], 5) + xy = onp.round(self.unit_complex_xy[..., 2:], 5) + return f"{self.__class__.__name__}(unit_complex={unit_complex}, xy={xy})" + +
+[docs] + @staticmethod + def from_xy_theta(x: hints.Scalar, y: hints.Scalar, theta: hints.Scalar) -> "SE2": + """Construct a transformation from standard 2D pose parameters. + + This is not the same as integrating over a length-3 twist. + """ + cos = onp.cos(theta) + sin = onp.sin(theta) + return SE2(unit_complex_xy=onp.stack([cos, sin, x, y], axis=-1))
+ + + # SE-specific. + +
+[docs] + @classmethod + @override + def from_rotation_and_translation( + cls, + rotation: SO2, + translation: onpt.NDArray[onp.floating], + ) -> "SE2": + assert translation.shape[-1:] == (2,) + rotation, translation = broadcast_leading_axes((rotation, translation)) + return SE2( + unit_complex_xy=onp.concatenate( + [rotation.unit_complex, translation], axis=-1 + ) + )
+ + +
+[docs] + @override + def rotation(self) -> SO2: + return SO2(unit_complex=self.unit_complex_xy[..., :2])
+ + +
+[docs] + @override + def translation(self) -> onpt.NDArray[onp.floating]: + return self.unit_complex_xy[..., 2:]
+ + + # Factory. + +
+[docs] + @classmethod + @override + def identity(cls, batch_axes: Tuple[int, ...] = ()) -> "SE2": + return SE2( + unit_complex_xy=onp.broadcast_to( + onp.array([1.0, 0.0, 0.0, 0.0]), (*batch_axes, 4) + ) + )
+ + +
+[docs] + @classmethod + @override + def from_matrix(cls, matrix: onpt.NDArray[onp.floating]) -> "SE2": + assert matrix.shape[-2:] == (3, 3) or matrix.shape[-2:] == (2, 3) + # Currently assumes bottom row is [0, 0, 1]. + return SE2.from_rotation_and_translation( + rotation=SO2.from_matrix(matrix[..., :2, :2]), + translation=matrix[..., :2, 2], + )
+ + + # Accessors. + +
+[docs] + @override + def parameters(self) -> onpt.NDArray[onp.floating]: + return self.unit_complex_xy
+ + +
+[docs] + @override + def as_matrix(self) -> onpt.NDArray[onp.floating]: + cos, sin, x, y = onp.moveaxis(self.unit_complex_xy, -1, 0) + out = onp.stack( + [ + cos, + -sin, + x, + sin, + cos, + y, + onp.zeros_like(x), + onp.zeros_like(x), + onp.ones_like(x), + ], + axis=-1, + ).reshape((*self.get_batch_axes(), 3, 3)) + return out
+ + + # Operations. + +
+[docs] + @classmethod + @override + def exp(cls, tangent: onpt.NDArray[onp.floating]) -> "SE2": + # Reference: + # > https://github.com/strasdat/Sophus/blob/a0fe89a323e20c42d3cecb590937eb7a06b8343a/sophus/se2.hpp#L558 + # Also see: + # > http://ethaneade.com/lie.pdf + + assert tangent.shape[-1:] == (3,) + + theta = tangent[..., 2] + use_taylor = onp.abs(theta) < get_epsilon(tangent.dtype) + + # Shim to avoid NaNs in onp.where branches, which cause failures for + # reverse-mode AD in JAX. This isn't needed for vanilla numpy. + safe_theta = cast( + onp.ndarray, + onp.where( + use_taylor, + onp.ones_like(theta), # Any non-zero value should do here. + theta, + ), + ) + + theta_sq = theta**2 + sin_over_theta = cast( + onp.ndarray, + onp.where( + use_taylor, + 1.0 - theta_sq / 6.0, + onp.sin(safe_theta) / safe_theta, + ), + ) + one_minus_cos_over_theta = cast( + onp.ndarray, + onp.where( + use_taylor, + 0.5 * theta - theta * theta_sq / 24.0, + (1.0 - onp.cos(safe_theta)) / safe_theta, + ), + ) + + V = onp.stack( + [ + sin_over_theta, + -one_minus_cos_over_theta, + one_minus_cos_over_theta, + sin_over_theta, + ], + axis=-1, + ).reshape((*tangent.shape[:-1], 2, 2)) + return SE2.from_rotation_and_translation( + rotation=SO2.from_radians(theta), + translation=onp.einsum("...ij,...j->...i", V, tangent[..., :2]), + )
+ + +
+[docs] + @override + def log(self) -> onpt.NDArray[onp.floating]: + # Reference: + # > https://github.com/strasdat/Sophus/blob/a0fe89a323e20c42d3cecb590937eb7a06b8343a/sophus/se2.hpp#L160 + # Also see: + # > http://ethaneade.com/lie.pdf + + theta = self.rotation().log()[..., 0] + + cos = onp.cos(theta) + cos_minus_one = cos - 1.0 + half_theta = theta / 2.0 + use_taylor = onp.abs(cos_minus_one) < get_epsilon(theta.dtype) + + # Shim to avoid NaNs in onp.where branches, which cause failures for + # reverse-mode AD in JAX. This isn't needed for vanilla numpy. + safe_cos_minus_one = onp.where( + use_taylor, + onp.ones_like(cos_minus_one), # Any non-zero value should do here. + cos_minus_one, + ) + + half_theta_over_tan_half_theta = onp.where( + use_taylor, + # Taylor approximation. + 1.0 - theta**2 / 12.0, + # Default. + -(half_theta * onp.sin(theta)) / safe_cos_minus_one, + ) + + V_inv = onp.stack( + [ + half_theta_over_tan_half_theta, + half_theta, + -half_theta, + half_theta_over_tan_half_theta, + ], + axis=-1, + ).reshape((*theta.shape, 2, 2)) + + tangent = onp.concatenate( + [ + onp.einsum("...ij,...j->...i", V_inv, self.translation()), + theta[..., None], + ], + axis=-1, + ) + return tangent
+ + +
+[docs] + @override + def adjoint(self: "SE2") -> onpt.NDArray[onp.floating]: + cos, sin, x, y = onp.moveaxis(self.unit_complex_xy, -1, 0) + return onp.stack( + [ + cos, + -sin, + y, + sin, + cos, + -x, + onp.zeros_like(x), + onp.zeros_like(x), + onp.ones_like(x), + ], + axis=-1, + ).reshape((*self.get_batch_axes(), 3, 3))
+
+ + + # @classmethod + # @override + # def sample_uniform( + # cls, key: onp.ndarray, batch_axes: jdc.Static[Tuple[int, ...]] = () + # ) -> "SE2": + # key0, key1 = jax.random.split(key) + # return SE2.from_rotation_and_translation( + # rotation=SO2.sample_uniform(key0, batch_axes=batch_axes), + # translation=jax.random.uniform( + # key=key1, + # shape=( + # *batch_axes, + # 2, + # ), + # minval=-1.0, + # maxval=1.0, + # ), + # ) +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/transforms/_se3/index.html b/versions/0.2.2/_modules/viser/transforms/_se3/index.html new file mode 100644 index 000000000..5cc61dc70 --- /dev/null +++ b/versions/0.2.2/_modules/viser/transforms/_se3/index.html @@ -0,0 +1,621 @@ + + + + + + + + + + + + viser.transforms._se3 - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser.transforms._se3

+from __future__ import annotations
+
+import dataclasses
+from typing import Tuple, cast
+
+import numpy as onp
+import numpy.typing as onpt
+from typing_extensions import override
+
+from . import _base
+from ._so3 import SO3
+from .utils import broadcast_leading_axes, get_epsilon, register_lie_group
+
+
+def _skew(omega: onpt.NDArray[onp.floating]) -> onpt.NDArray[onp.floating]:
+    """Returns the skew-symmetric form of a length-3 vector."""
+
+    wx, wy, wz = onp.moveaxis(omega, -1, 0)
+    zeros = onp.zeros_like(wx)
+    return onp.stack(
+        [zeros, -wz, wy, wz, zeros, -wx, -wy, wx, zeros],
+        axis=-1,
+    ).reshape((*omega.shape[:-1], 3, 3))
+
+
+
+[docs] +@register_lie_group( + matrix_dim=4, + parameters_dim=7, + tangent_dim=6, + space_dim=3, +) +@dataclasses.dataclass(frozen=True) +class SE3(_base.SEBase[SO3]): + """Special Euclidean group for proper rigid transforms in 3D. Broadcasting + rules are the same as for numpy. + + Ported to numpy from `jaxlie.SE3`. + + Internal parameterization is `(qw, qx, qy, qz, x, y, z)`. Tangent parameterization + is `(vx, vy, vz, omega_x, omega_y, omega_z)`. + """ + + # SE3-specific. + + wxyz_xyz: onpt.NDArray[onp.floating] + """Internal parameters. wxyz quaternion followed by xyz translation. Shape should be `(*, 7)`.""" + + @override + def __repr__(self) -> str: + quat = onp.round(self.wxyz_xyz[..., :4], 5) + trans = onp.round(self.wxyz_xyz[..., 4:], 5) + return f"{self.__class__.__name__}(wxyz={quat}, xyz={trans})" + + # SE-specific. + +
+[docs] + @classmethod + @override + def from_rotation_and_translation( + cls, + rotation: SO3, + translation: onpt.NDArray[onp.floating], + ) -> SE3: + assert translation.shape[-1:] == (3,) + rotation, translation = broadcast_leading_axes((rotation, translation)) + return SE3(wxyz_xyz=onp.concatenate([rotation.wxyz, translation], axis=-1))
+ + +
+[docs] + @override + def rotation(self) -> SO3: + return SO3(wxyz=self.wxyz_xyz[..., :4])
+ + +
+[docs] + @override + def translation(self) -> onpt.NDArray[onp.floating]: + return self.wxyz_xyz[..., 4:]
+ + + # Factory. + +
+[docs] + @classmethod + @override + def identity(cls, batch_axes: Tuple[int, ...] = ()) -> SE3: + return SE3( + wxyz_xyz=onp.broadcast_to( + onp.array([1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (*batch_axes, 7) + ) + )
+ + +
+[docs] + @classmethod + @override + def from_matrix(cls, matrix: onpt.NDArray[onp.floating]) -> SE3: + assert matrix.shape[-2:] == (4, 4) or matrix.shape[-2:] == (3, 4) + # Currently assumes bottom row is [0, 0, 0, 1]. + return SE3.from_rotation_and_translation( + rotation=SO3.from_matrix(matrix[..., :3, :3]), + translation=matrix[..., :3, 3], + )
+ + + # Accessors. + +
+[docs] + @override + def as_matrix(self) -> onpt.NDArray[onp.floating]: + out = onp.zeros((*self.get_batch_axes(), 4, 4)) + out[..., :3, :3] = self.rotation().as_matrix() + out[..., :3, 3] = self.translation() + out[..., 3, 3] = 1.0 + return out
+ + +
+[docs] + @override + def parameters(self) -> onpt.NDArray[onp.floating]: + return self.wxyz_xyz
+ + + # Operations. + +
+[docs] + @classmethod + @override + def exp(cls, tangent: onpt.NDArray[onp.floating]) -> SE3: + # Reference: + # > https://github.com/strasdat/Sophus/blob/a0fe89a323e20c42d3cecb590937eb7a06b8343a/sophus/se3.hpp#L761 + + # (x, y, z, omega_x, omega_y, omega_z) + assert tangent.shape[-1:] == (6,) + + rotation = SO3.exp(tangent[..., 3:]) + + theta_squared = onp.sum(onp.square(tangent[..., 3:]), axis=-1) + use_taylor = theta_squared < get_epsilon(theta_squared.dtype) + + # Shim to avoid NaNs in onp.where branches, which cause failures for + # reverse-mode AD in JAX. This isn't needed for vanilla numpy. + theta_squared_safe = cast( + onp.ndarray, + onp.where( + use_taylor, + onp.ones_like(theta_squared), # Any non-zero value should do here. + theta_squared, + ), + ) + del theta_squared + theta_safe = onp.sqrt(theta_squared_safe) + + skew_omega = _skew(tangent[..., 3:]) + V = onp.where( + use_taylor[..., None, None], + rotation.as_matrix(), + ( + onp.eye(3) + + ((1.0 - onp.cos(theta_safe)) / (theta_squared_safe))[..., None, None] + * skew_omega + + ( + (theta_safe - onp.sin(theta_safe)) + / (theta_squared_safe * theta_safe) + )[..., None, None] + * onp.einsum("...ij,...jk->...ik", skew_omega, skew_omega) + ), + ) + + return SE3.from_rotation_and_translation( + rotation=rotation, + translation=onp.einsum("...ij,...j->...i", V, tangent[..., :3]), + )
+ + +
+[docs] + @override + def log(self) -> onpt.NDArray[onp.floating]: + # Reference: + # > https://github.com/strasdat/Sophus/blob/a0fe89a323e20c42d3cecb590937eb7a06b8343a/sophus/se3.hpp#L223 + omega = self.rotation().log() + theta_squared = onp.sum(onp.square(omega), axis=-1) + use_taylor = theta_squared < get_epsilon(theta_squared.dtype) + + skew_omega = _skew(omega) + + # Shim to avoid NaNs in onp.where branches, which cause failures for + # reverse-mode AD in JAX. This isn't needed for vanilla numpy. + theta_squared_safe = onp.where( + use_taylor, + onp.ones_like(theta_squared), # Any non-zero value should do here. + theta_squared, + ) + del theta_squared + theta_safe = onp.sqrt(theta_squared_safe) + half_theta_safe = theta_safe / 2.0 + + V_inv = onp.where( + use_taylor[..., None, None], + onp.eye(3) + - 0.5 * skew_omega + + onp.einsum("...ij,...jk->...ik", skew_omega, skew_omega) / 12.0, + ( + onp.eye(3) + - 0.5 * skew_omega + + ( + ( + 1.0 + - theta_safe + * onp.cos(half_theta_safe) + / (2.0 * onp.sin(half_theta_safe)) + ) + / theta_squared_safe + )[..., None, None] + * onp.einsum("...ij,...jk->...ik", skew_omega, skew_omega) + ), + ) + return onp.concatenate( + [onp.einsum("...ij,...j->...i", V_inv, self.translation()), omega], axis=-1 + )
+ + +
+[docs] + @override + def adjoint(self) -> onpt.NDArray[onp.floating]: + R = self.rotation().as_matrix() + return onp.concatenate( + [ + onp.concatenate( + [R, onp.einsum("...ij,...jk->...ik", _skew(self.translation()), R)], + axis=-1, + ), + onp.concatenate( + [onp.zeros((*self.get_batch_axes(), 3, 3)), R], axis=-1 + ), + ], + axis=-2, + )
+
+ + + # @classmethod + # @override + # def sample_uniform( + # cls, key: onp.ndarray, batch_axes: jdc.Static[Tuple[int, ...]] = () + # ) -> SE3: + # key0, key1 = jax.random.split(key) + # return SE3.from_rotation_and_translation( + # rotation=SO3.sample_uniform(key0, batch_axes=batch_axes), + # translation=jax.random.uniform( + # key=key1, shape=(*batch_axes, 3), minval=-1.0, maxval=1.0 + # ), + # ) +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/transforms/_so2/index.html b/versions/0.2.2/_modules/viser/transforms/_so2/index.html new file mode 100644 index 000000000..18ec95708 --- /dev/null +++ b/versions/0.2.2/_modules/viser/transforms/_so2/index.html @@ -0,0 +1,543 @@ + + + + + + + + + + + + viser.transforms._so2 - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser.transforms._so2

+from __future__ import annotations
+
+import dataclasses
+from typing import Tuple
+
+import numpy as onp
+import numpy.typing as onpt
+from typing_extensions import override
+
+from . import _base, hints
+from .utils import broadcast_leading_axes, register_lie_group
+
+
+
+[docs] +@register_lie_group( + matrix_dim=2, + parameters_dim=2, + tangent_dim=1, + space_dim=2, +) +@dataclasses.dataclass(frozen=True) +class SO2(_base.SOBase): + """Special orthogonal group for 2D rotations. Broadcasting rules are the + same as for numpy. + + Ported to numpy from `jaxlie.SO2`. + + Internal parameterization is `(cos, sin)`. Tangent parameterization is `(omega,)`. + """ + + # SO2-specific. + + unit_complex: onpt.NDArray[onp.floating] + """Internal parameters. `(cos, sin)`. Shape should be `(*, 2)`.""" + + @override + def __repr__(self) -> str: + unit_complex = onp.round(self.unit_complex, 5) + return f"{self.__class__.__name__}(unit_complex={unit_complex})" + +
+[docs] + @staticmethod + def from_radians(theta: hints.Scalar) -> SO2: + """Construct a rotation object from a scalar angle.""" + cos = onp.cos(theta) + sin = onp.sin(theta) + return SO2(unit_complex=onp.stack([cos, sin], axis=-1))
+ + +
+[docs] + def as_radians(self) -> onpt.NDArray[onp.floating]: + """Compute a scalar angle from a rotation object.""" + radians = self.log()[..., 0] + return radians
+ + + # Factory. + +
+[docs] + @classmethod + @override + def identity(cls, batch_axes: Tuple[int, ...] = ()) -> SO2: + return SO2( + unit_complex=onp.stack( + [onp.ones(batch_axes), onp.zeros(batch_axes)], axis=-1 + ) + )
+ + +
+[docs] + @classmethod + @override + def from_matrix(cls, matrix: onpt.NDArray[onp.floating]) -> SO2: + assert matrix.shape[-2:] == (2, 2) + return SO2(unit_complex=onp.asarray(matrix[..., :, 0]))
+ + + # Accessors. + +
+[docs] + @override + def as_matrix(self) -> onpt.NDArray[onp.floating]: + cos_sin = self.unit_complex + out = onp.stack( + [ + # [cos, -sin], + cos_sin * onp.array([1, -1]), + # [sin, cos], + cos_sin[..., ::-1], + ], + axis=-2, + ) + assert out.shape == (*self.get_batch_axes(), 2, 2) + return out # type: ignore
+ + +
+[docs] + @override + def parameters(self) -> onpt.NDArray[onp.floating]: + return self.unit_complex
+ + + # Operations. + +
+[docs] + @override + def apply(self, target: onpt.NDArray[onp.floating]) -> onpt.NDArray[onp.floating]: + assert target.shape[-1:] == (2,) + self, target = broadcast_leading_axes((self, target)) + return onp.einsum("...ij,...j->...i", self.as_matrix(), target)
+ + +
+[docs] + @override + def multiply(self, other: SO2) -> SO2: + return SO2( + unit_complex=onp.einsum( + "...ij,...j->...i", self.as_matrix(), other.unit_complex + ) + )
+ + +
+[docs] + @classmethod + @override + def exp(cls, tangent: onpt.NDArray[onp.floating]) -> SO2: + assert tangent.shape[-1] == 1 + cos = onp.cos(tangent) + sin = onp.sin(tangent) + return SO2(unit_complex=onp.concatenate([cos, sin], axis=-1))
+ + +
+[docs] + @override + def log(self) -> onpt.NDArray[onp.floating]: + return onp.arctan2( + self.unit_complex[..., 1, None], self.unit_complex[..., 0, None] + )
+ + +
+[docs] + @override + def adjoint(self) -> onpt.NDArray[onp.floating]: + return onp.ones((*self.get_batch_axes(), 1, 1))
+ + +
+[docs] + @override + def inverse(self) -> SO2: + return SO2(unit_complex=self.unit_complex * onp.array([1, -1]))
+ + +
+[docs] + @override + def normalize(self) -> SO2: + return SO2( + unit_complex=self.unit_complex + / onp.linalg.norm(self.unit_complex, axis=-1, keepdims=True) + )
+
+ + + # @classmethod + # @override + # def sample_uniform( + # cls, key: onp.ndarray, batch_axes: jdc.Static[Tuple[int, ...]] = () + # ) -> SO2: + # out = SO2.from_radians( + # jax.random.uniform( + # key=key, shape=batch_axes, minval=0.0, maxval=2.0 * onp.pi) + # ) + # assert out.get_batch_axes() == batch_axes + # return out +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_modules/viser/transforms/_so3/index.html b/versions/0.2.2/_modules/viser/transforms/_so3/index.html new file mode 100644 index 000000000..7f556e113 --- /dev/null +++ b/versions/0.2.2/_modules/viser/transforms/_so3/index.html @@ -0,0 +1,884 @@ + + + + + + + + + + + + viser.transforms._so3 - viser + + + + + + + + + + + + + + + + + + Contents + + + + + + Menu + + + + + + + + Expand + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
viser
+
+
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for viser.transforms._so3

+from __future__ import annotations
+
+import dataclasses
+from typing import Tuple
+
+import numpy as onp
+import numpy.typing as onpt
+from typing_extensions import override
+
+from . import _base, hints
+from .utils import broadcast_leading_axes, get_epsilon, register_lie_group
+
+
+@dataclasses.dataclass(frozen=True)
+class RollPitchYaw:
+    """Struct containing roll, pitch, and yaw Euler angles."""
+
+    roll: onpt.NDArray[onp.floating]
+    pitch: onpt.NDArray[onp.floating]
+    yaw: onpt.NDArray[onp.floating]
+
+
+
+[docs] +@register_lie_group( + matrix_dim=3, + parameters_dim=4, + tangent_dim=3, + space_dim=3, +) +@dataclasses.dataclass(frozen=True) +class SO3(_base.SOBase): + """Special orthogonal group for 3D rotations. Broadcasting rules are the same as + for numpy. + + Ported to numpy from `jaxlie.SO3`. + + Internal parameterization is `(qw, qx, qy, qz)`. Tangent parameterization is + `(omega_x, omega_y, omega_z)`. + """ + + wxyz: onpt.NDArray[onp.floating] + """Internal parameters. `(w, x, y, z)` quaternion. Shape should be `(*, 4)`.""" + + @override + def __repr__(self) -> str: + wxyz = onp.round(self.wxyz, 5) + return f"{self.__class__.__name__}(wxyz={wxyz})" + +
+[docs] + @staticmethod + def from_x_radians(theta: hints.Scalar) -> SO3: + """Generates a x-axis rotation. + + Args: + angle: X rotation, in radians. + + Returns: + Output. + """ + zeros = onp.zeros_like(theta) + return SO3.exp(onp.stack([theta, zeros, zeros], axis=-1))
+ + +
+[docs] + @staticmethod + def from_y_radians(theta: hints.Scalar) -> SO3: + """Generates a y-axis rotation. + + Args: + angle: Y rotation, in radians. + + Returns: + Output. + """ + zeros = onp.zeros_like(theta) + return SO3.exp(onp.stack([zeros, theta, zeros], axis=-1))
+ + +
+[docs] + @staticmethod + def from_z_radians(theta: hints.Scalar) -> SO3: + """Generates a z-axis rotation. + + Args: + angle: Z rotation, in radians. + + Returns: + Output. + """ + zeros = onp.zeros_like(theta) + return SO3.exp(onp.stack([zeros, zeros, theta], axis=-1))
+ + +
+[docs] + @staticmethod + def from_rpy_radians( + roll: hints.Scalar, + pitch: hints.Scalar, + yaw: hints.Scalar, + ) -> SO3: + """Generates a transform from a set of Euler angles. Uses the ZYX mobile robot + convention. + + Args: + roll: X rotation, in radians. Applied first. + pitch: Y rotation, in radians. Applied second. + yaw: Z rotation, in radians. Applied last. + + Returns: + Output. + """ + return ( + SO3.from_z_radians(yaw) +
+[docs] + @ SO3.from_y_radians(pitch) + @ SO3.from_x_radians(roll) + )
+ + + @staticmethod + def from_quaternion_xyzw(xyzw: onpt.NDArray[onp.floating]) -> SO3: + """Construct a rotation from an `xyzw` quaternion. + + Note that `wxyz` quaternions can be constructed using the default dataclass + constructor. + + Args: + xyzw: xyzw quaternion. Shape should be (*, 4). + + Returns: + Output. + """ + assert xyzw.shape[-1:] == (4,) + return SO3(onp.roll(xyzw, axis=-1, shift=1))
+ + +
+[docs] + def as_quaternion_xyzw(self) -> onpt.NDArray[onp.floating]: + """Grab parameters as xyzw quaternion.""" + return onp.roll(self.wxyz, axis=-1, shift=-1)
+ + +
+[docs] + def as_rpy_radians(self) -> RollPitchYaw: + """Computes roll, pitch, and yaw angles. Uses the ZYX mobile robot convention. + + Returns: + Named tuple containing Euler angles in radians. + """ + return RollPitchYaw( + roll=self.compute_roll_radians(), + pitch=self.compute_pitch_radians(), + yaw=self.compute_yaw_radians(), + )
+ + +
+[docs] + def compute_roll_radians(self) -> onpt.NDArray[onp.floating]: + """Compute roll angle. Uses the ZYX mobile robot convention. + + Returns: + Euler angle in radians. + """ + # https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_angles_conversion + q0, q1, q2, q3 = onp.moveaxis(self.wxyz, -1, 0) + return onp.arctan2(2 * (q0 * q1 + q2 * q3), 1 - 2 * (q1**2 + q2**2))
+ + +
+[docs] + def compute_pitch_radians(self) -> onpt.NDArray[onp.floating]: + """Compute pitch angle. Uses the ZYX mobile robot convention. + + Returns: + Euler angle in radians. + """ + # https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_angles_conversion + q0, q1, q2, q3 = onp.moveaxis(self.wxyz, -1, 0) + return onp.arcsin(2 * (q0 * q2 - q3 * q1))
+ + +
+[docs] + def compute_yaw_radians(self) -> onpt.NDArray[onp.floating]: + """Compute yaw angle. Uses the ZYX mobile robot convention. + + Returns: + Euler angle in radians. + """ + # https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_angles_conversion + q0, q1, q2, q3 = onp.moveaxis(self.wxyz, -1, 0) + return onp.arctan2(2 * (q0 * q3 + q1 * q2), 1 - 2 * (q2**2 + q3**2))
+ + + # Factory. + +
+[docs] + @classmethod + @override + def identity(cls, batch_axes: Tuple[int, ...] = ()) -> SO3: + return SO3( + wxyz=onp.broadcast_to(onp.array([1.0, 0.0, 0.0, 0.0]), (*batch_axes, 4)) + )
+ + +
+[docs] + @classmethod + @override + def from_matrix(cls, matrix: onpt.NDArray[onp.floating]) -> SO3: + assert matrix.shape[-2:] == (3, 3) + + # Modified from: + # > "Converting a Rotation Matrix to a Quaternion" from Mike Day + # > https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf + + def case0(m): + t = 1 + m[..., 0, 0] - m[..., 1, 1] - m[..., 2, 2] + q = onp.stack( + [ + m[..., 2, 1] - m[..., 1, 2], + t, + m[..., 1, 0] + m[..., 0, 1], + m[..., 0, 2] + m[..., 2, 0], + ], + axis=-1, + ) + return t, q + + def case1(m): + t = 1 - m[..., 0, 0] + m[..., 1, 1] - m[..., 2, 2] + q = onp.stack( + [ + m[..., 0, 2] - m[..., 2, 0], + m[..., 1, 0] + m[..., 0, 1], + t, + m[..., 2, 1] + m[..., 1, 2], + ], + axis=-1, + ) + return t, q + + def case2(m): + t = 1 - m[..., 0, 0] - m[..., 1, 1] + m[..., 2, 2] + q = onp.stack( + [ + m[..., 1, 0] - m[..., 0, 1], + m[..., 0, 2] + m[..., 2, 0], + m[..., 2, 1] + m[..., 1, 2], + t, + ], + axis=-1, + ) + return t, q + + def case3(m): + t = 1 + m[..., 0, 0] + m[..., 1, 1] + m[..., 2, 2] + q = onp.stack( + [ + t, + m[..., 2, 1] - m[..., 1, 2], + m[..., 0, 2] - m[..., 2, 0], + m[..., 1, 0] - m[..., 0, 1], + ], + axis=-1, + ) + return t, q + + # Compute four cases, then pick the most precise one. + # Probably worth revisiting this! + case0_t, case0_q = case0(matrix) + case1_t, case1_q = case1(matrix) + case2_t, case2_q = case2(matrix) + case3_t, case3_q = case3(matrix) + + cond0 = matrix[..., 2, 2] < 0 + cond1 = matrix[..., 0, 0] > matrix[..., 1, 1] + cond2 = matrix[..., 0, 0] < -matrix[..., 1, 1] + + t = onp.where( + cond0, + onp.where(cond1, case0_t, case1_t), + onp.where(cond2, case2_t, case3_t), + ) + q = onp.where( + cond0[..., None], + onp.where(cond1[..., None], case0_q, case1_q), + onp.where(cond2[..., None], case2_q, case3_q), + ) + + # We can also choose to branch, but this is slower. + # t, q = jax.lax.cond( + # matrix[2, 2] < 0, + # true_fun=lambda matrix: jax.lax.cond( + # matrix[0, 0] > matrix[1, 1], + # true_fun=case0, + # false_fun=case1, + # operand=matrix, + # ), + # false_fun=lambda matrix: jax.lax.cond( + # matrix[0, 0] < -matrix[1, 1], + # true_fun=case2, + # false_fun=case3, + # operand=matrix, + # ), + # operand=matrix, + # ) + + return SO3(wxyz=q * 0.5 / onp.sqrt(t[..., None]))
+ + + # Accessors. + +
+[docs] + @override + def as_matrix(self) -> onpt.NDArray[onp.floating]: + norm_sq = onp.sum(onp.square(self.wxyz), axis=-1, keepdims=True) + q = self.wxyz * onp.sqrt(2.0 / norm_sq) # (*, 4) + q_outer = onp.einsum("...i,...j->...ij", q, q) # (*, 4, 4) + return onp.stack( + [ + 1.0 - q_outer[..., 2, 2] - q_outer[..., 3, 3], + q_outer[..., 1, 2] - q_outer[..., 3, 0], + q_outer[..., 1, 3] + q_outer[..., 2, 0], + q_outer[..., 1, 2] + q_outer[..., 3, 0], + 1.0 - q_outer[..., 1, 1] - q_outer[..., 3, 3], + q_outer[..., 2, 3] - q_outer[..., 1, 0], + q_outer[..., 1, 3] - q_outer[..., 2, 0], + q_outer[..., 2, 3] + q_outer[..., 1, 0], + 1.0 - q_outer[..., 1, 1] - q_outer[..., 2, 2], + ], + axis=-1, + ).reshape(*q.shape[:-1], 3, 3)
+ + +
+[docs] + @override + def parameters(self) -> onpt.NDArray[onp.floating]: + return self.wxyz
+ + + # Operations. + +
+[docs] + @override + def apply(self, target: onpt.NDArray[onp.floating]) -> onpt.NDArray[onp.floating]: + assert target.shape[-1:] == (3,) + self, target = broadcast_leading_axes((self, target)) + + # Compute using quaternion multiplys. + padded_target = onp.concatenate( + [onp.zeros((*self.get_batch_axes(), 1)), target], axis=-1 + ) + return (self @ SO3(wxyz=padded_target) @ self.inverse()).wxyz[..., 1:]
+ + +
+[docs] + @override + def multiply(self, other: SO3) -> SO3: + w0, x0, y0, z0 = onp.moveaxis(self.wxyz, -1, 0) + w1, x1, y1, z1 = onp.moveaxis(other.wxyz, -1, 0) + return SO3( + wxyz=onp.stack( + [ + -x0 * x1 - y0 * y1 - z0 * z1 + w0 * w1, + x0 * w1 + y0 * z1 - z0 * y1 + w0 * x1, + -x0 * z1 + y0 * w1 + z0 * x1 + w0 * y1, + x0 * y1 - y0 * x1 + z0 * w1 + w0 * z1, + ], + axis=-1, + ) + )
+ + +
+[docs] + @classmethod + @override + def exp(cls, tangent: onpt.NDArray[onp.floating]) -> SO3: + # Reference: + # > https://github.com/strasdat/Sophus/blob/a0fe89a323e20c42d3cecb590937eb7a06b8343a/sophus/so3.hpp#L583 + + assert tangent.shape[-1:] == (3,) + + theta_squared = onp.sum(onp.square(tangent), axis=-1) + theta_pow_4 = theta_squared * theta_squared + use_taylor = theta_squared < get_epsilon(tangent.dtype) + + # Shim to avoid NaNs in onp.where branches, which cause failures for + # reverse-mode AD in JAX. This isn't needed for vanilla numpy. + safe_theta = onp.sqrt( + onp.where( + use_taylor, + onp.ones_like(theta_squared), # Any constant value should do here. + theta_squared, + ) + ) + safe_half_theta = 0.5 * safe_theta + + real_factor = onp.where( + use_taylor, + 1.0 - theta_squared / 8.0 + theta_pow_4 / 384.0, + onp.cos(safe_half_theta), + ) + + imaginary_factor = onp.where( + use_taylor, + 0.5 - theta_squared / 48.0 + theta_pow_4 / 3840.0, + onp.sin(safe_half_theta) / safe_theta, + ) + + return SO3( + wxyz=onp.concatenate( + [ + real_factor[..., None], + imaginary_factor[..., None] * tangent, + ], + axis=-1, + ) + )
+ + +
+[docs] + @override + def log(self) -> onpt.NDArray[onp.floating]: + # Reference: + # > https://github.com/strasdat/Sophus/blob/a0fe89a323e20c42d3cecb590937eb7a06b8343a/sophus/so3.hpp#L247 + + w = self.wxyz[..., 0] + norm_sq = onp.sum(onp.square(self.wxyz[..., 1:]), axis=-1) + use_taylor = norm_sq < get_epsilon(norm_sq.dtype) + + # Shim to avoid NaNs in onp.where branches, which cause failures for + # reverse-mode AD in JAX. This isn't needed for vanilla numpy. + norm_safe = onp.sqrt( + onp.where( + use_taylor, + 1.0, # Any non-zero value should do here. + norm_sq, + ) + ) + w_safe = onp.where(use_taylor, w, 1.0) + atan_n_over_w = onp.arctan2( + onp.where(w < 0, -norm_safe, norm_safe), + onp.abs(w), + ) + atan_factor = onp.where( + use_taylor, + 2.0 / w_safe - 2.0 / 3.0 * norm_sq / w_safe**3, + onp.where( + onp.abs(w) < get_epsilon(w.dtype), + onp.where(w > 0, 1.0, -1.0) * onp.pi / norm_safe, + 2.0 * atan_n_over_w / norm_safe, + ), + ) + + return atan_factor[..., None] * self.wxyz[..., 1:] # type: ignore
+ + +
+[docs] + @override + def adjoint(self) -> onpt.NDArray[onp.floating]: + return self.as_matrix()
+ + +
+[docs] + @override + def inverse(self) -> SO3: + # Negate complex terms. + return SO3(wxyz=self.wxyz * onp.array([1, -1, -1, -1]))
+ + +
+[docs] + @override + def normalize(self) -> SO3: + return SO3(wxyz=self.wxyz / onp.linalg.norm(self.wxyz, axis=-1, keepdims=True))
+
+ + + # @classmethod + # @override + # def sample_uniform( + # cls, key: onp.ndarray, batch_axes: jdc.Static[Tuple[int, ...]] = () + # ) -> SO3: + # # Uniformly sample over S^3. + # # > Reference: http://planning.cs.uiuc.edu/node198.html + # u1, u2, u3 = onp.moveaxis( + # jax.random.uniform( + # key=key, + # shape=(*batch_axes, 3), + # minval=onp.zeros(3), + # maxval=onp.array([1.0, 2.0 * onp.pi, 2.0 * onp.pi]), + # ), + # -1, + # 0, + # ) + # a = onp.sqrt(1.0 - u1) + # b = onp.sqrt(u1) + # + # return SO3( + # wxyz=onp.stack( + # [ + # a * onp.sin(u2), + # a * onp.cos(u2), + # b * onp.sin(u3), + # b * onp.cos(u3), + # ], + # axis=-1, + # ) + # ) +
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/versions/0.2.2/_sources/camera_handles.md.txt b/versions/0.2.2/_sources/camera_handles.md.txt new file mode 100644 index 000000000..857bb0602 --- /dev/null +++ b/versions/0.2.2/_sources/camera_handles.md.txt @@ -0,0 +1,10 @@ +# Camera Handles + + + +.. autoclass:: viser.CameraHandle + :members: + :undoc-members: + :inherited-members: + + diff --git a/versions/0.2.2/_sources/client_handles.md.txt b/versions/0.2.2/_sources/client_handles.md.txt new file mode 100644 index 000000000..5da650d7b --- /dev/null +++ b/versions/0.2.2/_sources/client_handles.md.txt @@ -0,0 +1,10 @@ +# Client Handles + + + +.. autoclass:: viser.ClientHandle + :members: + :undoc-members: + :inherited-members: + + diff --git a/versions/0.2.2/_sources/conventions.md.txt b/versions/0.2.2/_sources/conventions.md.txt new file mode 100644 index 000000000..590fd2daf --- /dev/null +++ b/versions/0.2.2/_sources/conventions.md.txt @@ -0,0 +1,62 @@ +# Frame Conventions + +In this note, we describe the coordinate frame conventions used in `viser`. + +## Scene tree naming + +Each object that we add to the scene in viser is instantiated as a node in a +scene tree. The structure of this tree is determined by the names assigned to +the nodes. + +If we add a coordinate frame called `/base_link/shoulder/wrist`, it signifies +three nodes: the `wrist` is a child of the `shoulder` which is a child of the +`base_link`. + +If we set the transformation of a given node like `/shoulder`, both it and all +of its children will move. Its parent, `/base_link`, will be unaffected. + +## Poses + +Poses in `viser` are defined using a pair of fields: + +- `wxyz`, a unit quaternion orientation term. This should always be 4D. +- `position`, a translation term. This should always be 3D. + +These correspond to a transformation from coordinates in the local frame to the +parent frame: + + + +.. math:: + + p_\mathrm{parent} = \begin{bmatrix} R \mid t \end{bmatrix}p_\mathrm{local} + + + +where `wxyz` is the quaternion form of the :math:`\mathrm{SO}(3)` matrix +:math:`R` and `position` is the translation term :math:`t`. + +## World coordinates + +In the world coordinate space, +Z points upward by default. This can be +overridden with :func:`viser.ViserServer.set_up_direction()` or +:func:`viser.ClientHandle.set_up_direction()`. + +## Cameras + +All camera parameters exposed to the Python API use the COLMAP/OpenCV +convention: + +- Forward: +Z +- Up: -Y +- Right: +X + +Confusingly, this is different from Nerfstudio, which adopts the OpenGL/Blender +convention: + +- Forward: -Z +- Up: +Y +- Right: +X + +Note that conversion between the two is a simple 180 degree rotation around the +X-axis. diff --git a/versions/0.2.2/_sources/development.md.txt b/versions/0.2.2/_sources/development.md.txt new file mode 100644 index 000000000..86d9a7597 --- /dev/null +++ b/versions/0.2.2/_sources/development.md.txt @@ -0,0 +1,118 @@ +# Development + +In this note, we outline current practices, tools, and workflows for `viser` +development. We assume that the repository is cloned to `~/viser`. + +## Python install + +We recommend an editable install for Python development, ideally in a virtual +environment (eg via conda). + +```bash +# Install package. +cd ~/viser +pip install -e . + +# Install example dependencies. +pip install -e .[examples] +``` + +After installation, any of the example scripts (`~/viser/examples`) should be +runnable. A few of them require downloading assets, which can be done via the +scripts in `~/viser/examples/assets`. + +**Linting, formatting, type-checking.** + +First, install developer tools: + +```bash +# Using pip. +pip install -e .[dev] +pre-commit install +``` + +It would be hard to write unit tests for `viser`. We rely on static typing for +robustness. To check your code, you can run the following: + +```bash +# runs linting, formatting, and type-checking +viser-dev-checks +``` + +## Message updates + +The `viser` frontend and backend communicate via a shared set of message +definitions: + +- On the server, these are defined as Python dataclasses in + `~/viser/src/viser/_messages.py`. +- On the client, these are defined as TypeScript interfaces in + `~/viser/src/viser/client/src/WebsocketMessages.tsx`. + +Note that there is a 1:1 correspondence between the dataclasses message types +and the TypeScript ones. + +The TypeScript definitions should not be manually modified. Instead, changes +should be made in Python and synchronized via the `sync_message_defs.py` script: + +``` +cd ~/viser +python sync_message_defs.py +``` + +## Client development + +For client development, we can start by launching a relevant Python script. The +examples are a good place to start: + +``` +cd ~/viser/examples +python 05_camera_commands.py +``` + +When a `viser` script is launched, two URLs will be printed: + +- An HTTP URL, like `http://localhost:8080`, which can be used to open a + _pre-built_ version of the React frontend. +- A websocket URL, like `ws://localhost:8080`, which client applications can + connect to. + +If changes to the client source files are detected on startup, `viser` will +re-build the client automatically. This is okay for quick changes, but for +faster iteration we can also launch a development version of the frontend, which +will reflect changes we make to the client source files +(`~/viser/src/viser/client/src`) without a full build. This requires a few more +steps. + +**Installing dependencies.** + +1. [Install nodejs.](https://nodejs.dev/en/download/package-manager) +2. [Install yarn.](https://yarnpkg.com/getting-started/install) +3. Install dependencies. + ``` + cd ~/viser/src/viser/client + yarn install + ``` + +**Launching client.** + +To launch the client, we can run: + +``` +cd ~/viser/src/viser/client +yarn start +``` + +from the `viser/src/viser/client` directory. After opening the client in a web +browser, the websocket server address typically needs to be updated in the +"Server" tab. + +**Formatting.** + +We use [prettier](https://prettier.io/docs/en/install.html). This can be run via +one of: + +- `prettier -w .` +- `npx prettier -w .` + +from `~/viser/client`. diff --git a/versions/0.2.2/_sources/events.md.txt b/versions/0.2.2/_sources/events.md.txt new file mode 100644 index 000000000..26e710ca3 --- /dev/null +++ b/versions/0.2.2/_sources/events.md.txt @@ -0,0 +1,14 @@ +# Events + +We define a small set of event types, which are passed to callback functions +when events like clicks or GUI updates are triggered. + + + +.. autoclass:: viser.ScenePointerEvent() + +.. autoclass:: viser.SceneNodePointerEvent() + +.. autoclass:: viser.GuiEvent() + + diff --git a/versions/0.2.2/_sources/examples/00_coordinate_frames.rst.txt b/versions/0.2.2/_sources/examples/00_coordinate_frames.rst.txt new file mode 100644 index 000000000..62a11f919 --- /dev/null +++ b/versions/0.2.2/_sources/examples/00_coordinate_frames.rst.txt @@ -0,0 +1,47 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Coordinate frames +========================================== + + +In this basic example, we visualize a set of coordinate frames. + +Naming for all scene nodes are hierarchical; /tree/branch, for example, is defined +relative to /tree. + + + +.. code-block:: python + :linenos: + + + import random + import time + + import viser + + server = viser.ViserServer() + + while True: + # Add some coordinate frames to the scene. These will be visualized in the viewer. + server.scene.add_frame( + "/tree", + wxyz=(1.0, 0.0, 0.0, 0.0), + position=(random.random() * 2.0, 2.0, 0.2), + ) + server.scene.add_frame( + "/tree/branch", + wxyz=(1.0, 0.0, 0.0, 0.0), + position=(random.random() * 2.0, 2.0, 0.2), + ) + leaf = server.scene.add_frame( + "/tree/branch/leaf", + wxyz=(1.0, 0.0, 0.0, 0.0), + position=(random.random() * 2.0, 2.0, 0.2), + ) + time.sleep(5.0) + + # Remove the leaf node from the scene. + leaf.remove() + time.sleep(0.5) diff --git a/versions/0.2.2/_sources/examples/01_image.rst.txt b/versions/0.2.2/_sources/examples/01_image.rst.txt new file mode 100644 index 000000000..fae604cd5 --- /dev/null +++ b/versions/0.2.2/_sources/examples/01_image.rst.txt @@ -0,0 +1,65 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Images +========================================== + + +Example for sending images to the viewer. + +We can send backgrond images to display behind the viewer (useful for visualizing +NeRFs), or images to render as 3D textures. + + + +.. code-block:: python + :linenos: + + + import time + from pathlib import Path + + import imageio.v3 as iio + import numpy as onp + import viser + + + def main() -> None: + server = viser.ViserServer() + + # Add a background image. + server.scene.set_background_image( + iio.imread(Path(__file__).parent / "assets/Cal_logo.png"), + format="png", + ) + + # Add main image. + server.scene.add_image( + "/img", + iio.imread(Path(__file__).parent / "assets/Cal_logo.png"), + 4.0, + 4.0, + format="png", + wxyz=(1.0, 0.0, 0.0, 0.0), + position=(2.0, 2.0, 0.0), + ) + while True: + server.scene.add_image( + "/noise", + onp.random.randint( + 0, + 256, + size=(400, 400, 3), + dtype=onp.uint8, + ), + 4.0, + 4.0, + format="jpeg", + wxyz=(1.0, 0.0, 0.0, 0.0), + position=(2.0, 2.0, -1e-2), + ) + time.sleep(0.2) + + + if __name__ == "__main__": + main() diff --git a/versions/0.2.2/_sources/examples/02_gui.rst.txt b/versions/0.2.2/_sources/examples/02_gui.rst.txt new file mode 100644 index 000000000..2d3178eb6 --- /dev/null +++ b/versions/0.2.2/_sources/examples/02_gui.rst.txt @@ -0,0 +1,140 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +GUI basics +========================================== + + +Examples of basic GUI elements that we can create, read from, and write to. + + + +.. code-block:: python + :linenos: + + + import time + + import numpy as onp + import viser + + + def main() -> None: + server = viser.ViserServer() + + # Add some common GUI elements: number inputs, sliders, vectors, checkboxes. + with server.gui.add_folder("Read-only"): + gui_counter = server.gui.add_number( + "Counter", + initial_value=0, + disabled=True, + ) + + gui_slider = server.gui.add_slider( + "Slider", + min=0, + max=100, + step=1, + initial_value=0, + disabled=True, + ) + + with server.gui.add_folder("Editable"): + gui_vector2 = server.gui.add_vector2( + "Position", + initial_value=(0.0, 0.0), + step=0.1, + ) + gui_vector3 = server.gui.add_vector3( + "Size", + initial_value=(1.0, 1.0, 1.0), + step=0.25, + ) + with server.gui.add_folder("Text toggle"): + gui_checkbox_hide = server.gui.add_checkbox( + "Hide", + initial_value=False, + ) + gui_text = server.gui.add_text( + "Text", + initial_value="Hello world", + ) + gui_button = server.gui.add_button("Button") + gui_checkbox_disable = server.gui.add_checkbox( + "Disable", + initial_value=False, + ) + gui_rgb = server.gui.add_rgb( + "Color", + initial_value=(255, 255, 0), + ) + gui_multi_slider = server.gui.add_multi_slider( + "Multi slider", + min=0, + max=100, + step=1, + initial_value=(0, 30, 100), + marks=((0, "0"), (50, "5"), (70, "7"), 99), + ) + gui_slider_positions = server.gui.add_slider( + "# sliders", + min=0, + max=10, + step=1, + initial_value=3, + marks=((0, "0"), (5, "5"), (7, "7"), 10), + ) + gui_upload_button = server.gui.add_upload_button( + "Upload", icon=viser.Icon.UPLOAD + ) + + @gui_upload_button.on_upload + def _(_) -> None: + """Callback for when a file is uploaded.""" + file = gui_upload_button.value + print(file.name, len(file.content), "bytes") + + # Pre-generate a point cloud to send. + point_positions = onp.random.uniform(low=-1.0, high=1.0, size=(5000, 3)) + color_coeffs = onp.random.uniform(0.4, 1.0, size=(point_positions.shape[0])) + + counter = 0 + while True: + # We can set the value of an input to a particular value. Changes are + # automatically reflected in connected clients. + gui_counter.value = counter + gui_slider.value = counter % 100 + + # We can set the position of a scene node with `.position`, and read the value + # of a gui element with `.value`. Changes are automatically reflected in + # connected clients. + server.scene.add_point_cloud( + "/point_cloud", + points=point_positions * onp.array(gui_vector3.value, dtype=onp.float32), + colors=( + onp.tile(gui_rgb.value, point_positions.shape[0]).reshape((-1, 3)) + * color_coeffs[:, None] + ).astype(onp.uint8), + position=gui_vector2.value + (0,), + point_shape="circle", + ) + + # We can use `.visible` and `.disabled` to toggle GUI elements. + gui_text.visible = not gui_checkbox_hide.value + gui_button.visible = not gui_checkbox_hide.value + gui_rgb.disabled = gui_checkbox_disable.value + gui_button.disabled = gui_checkbox_disable.value + gui_upload_button.disabled = gui_checkbox_disable.value + + # Update the number of handles in the multi-slider. + if gui_slider_positions.value != len(gui_multi_slider.value): + gui_multi_slider.value = onp.linspace( + 0, 100, gui_slider_positions.value, dtype=onp.int64 + ) + + counter += 1 + time.sleep(0.01) + + + if __name__ == "__main__": + main() diff --git a/versions/0.2.2/_sources/examples/03_gui_callbacks.rst.txt b/versions/0.2.2/_sources/examples/03_gui_callbacks.rst.txt new file mode 100644 index 000000000..615f8ec8f --- /dev/null +++ b/versions/0.2.2/_sources/examples/03_gui_callbacks.rst.txt @@ -0,0 +1,122 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +GUI callbacks +========================================== + + +Asynchronous usage of GUI elements: we can attach callbacks that are called as soon as +we get updates. + + + +.. code-block:: python + :linenos: + + + import time + + import numpy as onp + import viser + from typing_extensions import assert_never + + + def main() -> None: + server = viser.ViserServer() + + gui_reset_scene = server.gui.add_button("Reset Scene") + + gui_plane = server.gui.add_dropdown( + "Grid plane", ("xz", "xy", "yx", "yz", "zx", "zy") + ) + + def update_plane() -> None: + server.scene.add_grid( + "/grid", + width=10.0, + height=20.0, + width_segments=10, + height_segments=20, + plane=gui_plane.value, + ) + + gui_plane.on_update(lambda _: update_plane()) + + with server.gui.add_folder("Control"): + gui_show_frame = server.gui.add_checkbox("Show Frame", initial_value=True) + gui_show_everything = server.gui.add_checkbox( + "Show Everything", initial_value=True + ) + gui_axis = server.gui.add_dropdown("Axis", ("x", "y", "z")) + gui_include_z = server.gui.add_checkbox("Z in dropdown", initial_value=True) + + @gui_include_z.on_update + def _(_) -> None: + gui_axis.options = ("x", "y", "z") if gui_include_z.value else ("x", "y") + + with server.gui.add_folder("Sliders"): + gui_location = server.gui.add_slider( + "Location", min=-5.0, max=5.0, step=0.05, initial_value=0.0 + ) + gui_num_points = server.gui.add_slider( + "# Points", min=1000, max=200_000, step=1000, initial_value=10_000 + ) + + def draw_frame() -> None: + axis = gui_axis.value + if axis == "x": + pos = (gui_location.value, 0.0, 0.0) + elif axis == "y": + pos = (0.0, gui_location.value, 0.0) + elif axis == "z": + pos = (0.0, 0.0, gui_location.value) + else: + assert_never(axis) + + server.scene.add_frame( + "/frame", + wxyz=(1.0, 0.0, 0.0, 0.0), + position=pos, + show_axes=gui_show_frame.value, + axes_length=5.0, + ) + + def draw_points() -> None: + num_points = gui_num_points.value + server.scene.add_point_cloud( + "/frame/point_cloud", + points=onp.random.normal(size=(num_points, 3)), + colors=onp.random.randint(0, 256, size=(num_points, 3)), + ) + + # We can (optionally) also attach callbacks! + # Here, we update the point clouds + frames whenever any of the GUI items are updated. + gui_show_frame.on_update(lambda _: draw_frame()) + gui_show_everything.on_update( + lambda _: server.scene.set_global_visibility(gui_show_everything.value) + ) + gui_axis.on_update(lambda _: draw_frame()) + gui_location.on_update(lambda _: draw_frame()) + gui_num_points.on_update(lambda _: draw_points()) + + @gui_reset_scene.on_click + def _(_) -> None: + """Reset the scene when the reset button is clicked.""" + gui_show_frame.value = True + gui_location.value = 0.0 + gui_axis.value = "x" + gui_num_points.value = 10_000 + + draw_frame() + draw_points() + + # Finally, let's add the initial frame + point cloud and just loop infinitely. :) + update_plane() + draw_frame() + draw_points() + while True: + time.sleep(1.0) + + + if __name__ == "__main__": + main() diff --git a/versions/0.2.2/_sources/examples/04_camera_poses.rst.txt b/versions/0.2.2/_sources/examples/04_camera_poses.rst.txt new file mode 100644 index 000000000..2676e96d7 --- /dev/null +++ b/versions/0.2.2/_sources/examples/04_camera_poses.rst.txt @@ -0,0 +1,51 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Camera poses +========================================== + + +Example showing how we can detect new clients and read camera poses from them. + + + +.. code-block:: python + :linenos: + + + import time + + import viser + + server = viser.ViserServer() + server.scene.world_axes.visible = True + + + @server.on_client_connect + def _(client: viser.ClientHandle) -> None: + print("new client!") + + # This will run whenever we get a new camera! + @client.camera.on_update + def _(_: viser.CameraHandle) -> None: + print(f"New camera on client {client.client_id}!") + + # Show the client ID in the GUI. + gui_info = client.gui.add_text("Client ID", initial_value=str(client.client_id)) + gui_info.disabled = True + + + while True: + # Get all currently connected clients. + clients = server.get_clients() + print("Connected client IDs", clients.keys()) + + for id, client in clients.items(): + print(f"Camera pose for client {id}") + print(f"\twxyz: {client.camera.wxyz}") + print(f"\tposition: {client.camera.position}") + print(f"\tfov: {client.camera.fov}") + print(f"\taspect: {client.camera.aspect}") + print(f"\tlast update: {client.camera.update_timestamp}") + + time.sleep(2.0) diff --git a/versions/0.2.2/_sources/examples/05_camera_commands.rst.txt b/versions/0.2.2/_sources/examples/05_camera_commands.rst.txt new file mode 100644 index 000000000..95ee19a07 --- /dev/null +++ b/versions/0.2.2/_sources/examples/05_camera_commands.rst.txt @@ -0,0 +1,81 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Camera commands +========================================== + + +In addition to reads, camera parameters also support writes. These are synced to the +corresponding client automatically. + + + +.. code-block:: python + :linenos: + + + import time + + import numpy as onp + import viser + import viser.transforms as tf + + server = viser.ViserServer() + num_frames = 20 + + + @server.on_client_connect + def _(client: viser.ClientHandle) -> None: + """For each client that connects, we create a set of random frames + a click handler for each frame. + + When a frame is clicked, we move the camera to the corresponding frame. + """ + + rng = onp.random.default_rng(0) + + def make_frame(i: int) -> None: + # Sample a random orientation + position. + wxyz = rng.normal(size=4) + wxyz /= onp.linalg.norm(wxyz) + position = rng.uniform(-3.0, 3.0, size=(3,)) + + # Create a coordinate frame and label. + frame = client.scene.add_frame(f"/frame_{i}", wxyz=wxyz, position=position) + client.scene.add_label(f"/frame_{i}/label", text=f"Frame {i}") + + # Move the camera when we click a frame. + @frame.on_click + def _(_): + T_world_current = tf.SE3.from_rotation_and_translation( + tf.SO3(client.camera.wxyz), client.camera.position + ) + T_world_target = tf.SE3.from_rotation_and_translation( + tf.SO3(frame.wxyz), frame.position + ) @ tf.SE3.from_translation(onp.array([0.0, 0.0, -0.5])) + + T_current_target = T_world_current.inverse() @ T_world_target + + for j in range(20): + T_world_set = T_world_current @ tf.SE3.exp( + T_current_target.log() * j / 19.0 + ) + + # We can atomically set the orientation and the position of the camera + # together to prevent jitter that might happen if one was set before the + # other. + with client.atomic(): + client.camera.wxyz = T_world_set.rotation().wxyz + client.camera.position = T_world_set.translation() + + client.flush() # Optional! + time.sleep(1.0 / 60.0) + + # Mouse interactions should orbit around the frame origin. + client.camera.look_at = frame.position + + for i in range(num_frames): + make_frame(i) + + + while True: + time.sleep(1.0) diff --git a/versions/0.2.2/_sources/examples/06_mesh.rst.txt b/versions/0.2.2/_sources/examples/06_mesh.rst.txt new file mode 100644 index 000000000..15c1e718b --- /dev/null +++ b/versions/0.2.2/_sources/examples/06_mesh.rst.txt @@ -0,0 +1,48 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Meshes +========================================== + + +Visualize a mesh. To get the demo data, see ``./assets/download_dragon_mesh.sh``. + + + +.. code-block:: python + :linenos: + + + import time + from pathlib import Path + + import numpy as onp + import trimesh + import viser + import viser.transforms as tf + + mesh = trimesh.load_mesh(str(Path(__file__).parent / "assets/dragon.obj")) + assert isinstance(mesh, trimesh.Trimesh) + mesh.apply_scale(0.05) + + vertices = mesh.vertices + faces = mesh.faces + print(f"Loaded mesh with {vertices.shape} vertices, {faces.shape} faces") + + server = viser.ViserServer() + server.scene.add_mesh_simple( + name="/simple", + vertices=vertices, + faces=faces, + wxyz=tf.SO3.from_x_radians(onp.pi / 2).wxyz, + position=(0.0, 0.0, 0.0), + ) + server.scene.add_mesh_trimesh( + name="/trimesh", + mesh=mesh.smoothed(), + wxyz=tf.SO3.from_x_radians(onp.pi / 2).wxyz, + position=(0.0, 5.0, 0.0), + ) + + while True: + time.sleep(10.0) diff --git a/versions/0.2.2/_sources/examples/07_record3d_visualizer.rst.txt b/versions/0.2.2/_sources/examples/07_record3d_visualizer.rst.txt new file mode 100644 index 000000000..e7f28be00 --- /dev/null +++ b/versions/0.2.2/_sources/examples/07_record3d_visualizer.rst.txt @@ -0,0 +1,153 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Record3D visualizer +========================================== + + +Parse and stream record3d captures. To get the demo data, see ``./assets/download_record3d_dance.sh``. + + + +.. code-block:: python + :linenos: + + + import time + from pathlib import Path + + import numpy as onp + import tyro + import viser + import viser.extras + import viser.transforms as tf + from tqdm.auto import tqdm + + + def main( + data_path: Path = Path(__file__).parent / "assets/record3d_dance", + downsample_factor: int = 4, + max_frames: int = 100, + share: bool = False, + ) -> None: + server = viser.ViserServer() + if share: + server.request_share_url() + + print("Loading frames!") + loader = viser.extras.Record3dLoader(data_path) + num_frames = min(max_frames, loader.num_frames()) + + # Add playback UI. + with server.gui.add_folder("Playback"): + gui_timestep = server.gui.add_slider( + "Timestep", + min=0, + max=num_frames - 1, + step=1, + initial_value=0, + disabled=True, + ) + gui_next_frame = server.gui.add_button("Next Frame", disabled=True) + gui_prev_frame = server.gui.add_button("Prev Frame", disabled=True) + gui_playing = server.gui.add_checkbox("Playing", True) + gui_framerate = server.gui.add_slider( + "FPS", min=1, max=60, step=0.1, initial_value=loader.fps + ) + gui_framerate_options = server.gui.add_button_group( + "FPS options", ("10", "20", "30", "60") + ) + + # Frame step buttons. + @gui_next_frame.on_click + def _(_) -> None: + gui_timestep.value = (gui_timestep.value + 1) % num_frames + + @gui_prev_frame.on_click + def _(_) -> None: + gui_timestep.value = (gui_timestep.value - 1) % num_frames + + # Disable frame controls when we're playing. + @gui_playing.on_update + def _(_) -> None: + gui_timestep.disabled = gui_playing.value + gui_next_frame.disabled = gui_playing.value + gui_prev_frame.disabled = gui_playing.value + + # Set the framerate when we click one of the options. + @gui_framerate_options.on_click + def _(_) -> None: + gui_framerate.value = int(gui_framerate_options.value) + + prev_timestep = gui_timestep.value + + # Toggle frame visibility when the timestep slider changes. + @gui_timestep.on_update + def _(_) -> None: + nonlocal prev_timestep + current_timestep = gui_timestep.value + with server.atomic(): + frame_nodes[current_timestep].visible = True + frame_nodes[prev_timestep].visible = False + prev_timestep = current_timestep + server.flush() # Optional! + + # Load in frames. + server.scene.add_frame( + "/frames", + wxyz=tf.SO3.exp(onp.array([onp.pi / 2.0, 0.0, 0.0])).wxyz, + position=(0, 0, 0), + show_axes=False, + ) + frame_nodes: list[viser.FrameHandle] = [] + for i in tqdm(range(num_frames)): + frame = loader.get_frame(i) + position, color = frame.get_point_cloud(downsample_factor) + + # Add base frame. + frame_nodes.append(server.scene.add_frame(f"/frames/t{i}", show_axes=False)) + + # Place the point cloud in the frame. + server.scene.add_point_cloud( + name=f"/frames/t{i}/point_cloud", + points=position, + colors=color, + point_size=0.01, + point_shape="rounded", + ) + + # Place the frustum. + fov = 2 * onp.arctan2(frame.rgb.shape[0] / 2, frame.K[0, 0]) + aspect = frame.rgb.shape[1] / frame.rgb.shape[0] + server.scene.add_camera_frustum( + f"/frames/t{i}/frustum", + fov=fov, + aspect=aspect, + scale=0.15, + image=frame.rgb[::downsample_factor, ::downsample_factor], + wxyz=tf.SO3.from_matrix(frame.T_world_camera[:3, :3]).wxyz, + position=frame.T_world_camera[:3, 3], + ) + + # Add some axes. + server.scene.add_frame( + f"/frames/t{i}/frustum/axes", + axes_length=0.05, + axes_radius=0.005, + ) + + # Hide all but the current frame. + for i, frame_node in enumerate(frame_nodes): + frame_node.visible = i == gui_timestep.value + + # Playback update loop. + prev_timestep = gui_timestep.value + while True: + if gui_playing.value: + gui_timestep.value = (gui_timestep.value + 1) % num_frames + + time.sleep(1.0 / gui_framerate.value) + + + if __name__ == "__main__": + tyro.cli(main) diff --git a/versions/0.2.2/_sources/examples/08_smpl_visualizer.rst.txt b/versions/0.2.2/_sources/examples/08_smpl_visualizer.rst.txt new file mode 100644 index 000000000..9883b78c1 --- /dev/null +++ b/versions/0.2.2/_sources/examples/08_smpl_visualizer.rst.txt @@ -0,0 +1,268 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Visualizer for SMPL human body models. Requires a .npz model file. +========================================== + + +See here for download instructions: + https://github.com/vchoutas/smplx?tab=readme-ov-file#downloading-the-model + + + +.. code-block:: python + :linenos: + + + from __future__ import annotations + + import time + from dataclasses import dataclass + from pathlib import Path + + import numpy as np + import numpy as onp + import tyro + import viser + import viser.transforms as tf + + + @dataclass(frozen=True) + class SmplOutputs: + vertices: np.ndarray + faces: np.ndarray + T_world_joint: np.ndarray # (num_joints, 4, 4) + T_parent_joint: np.ndarray # (num_joints, 4, 4) + + + class SmplHelper: + """Helper for models in the SMPL family, implemented in numpy.""" + + def __init__(self, model_path: Path) -> None: + assert model_path.suffix.lower() == ".npz", "Model should be an .npz file!" + body_dict = dict(**onp.load(model_path, allow_pickle=True)) + + self._J_regressor = body_dict["J_regressor"] + self._weights = body_dict["weights"] + self._v_template = body_dict["v_template"] + self._posedirs = body_dict["posedirs"] + self._shapedirs = body_dict["shapedirs"] + self._faces = body_dict["f"] + + self.num_joints: int = self._weights.shape[-1] + self.num_betas: int = self._shapedirs.shape[-1] + self.parent_idx: np.ndarray = body_dict["kintree_table"][0] + + def get_outputs(self, betas: np.ndarray, joint_rotmats: np.ndarray) -> SmplOutputs: + # Get shaped vertices + joint positions, when all local poses are identity. + v_tpose = self._v_template + np.einsum("vxb,b->vx", self._shapedirs, betas) + j_tpose = np.einsum("jv,vx->jx", self._J_regressor, v_tpose) + + # Local SE(3) transforms. + T_parent_joint = np.zeros((self.num_joints, 4, 4)) + np.eye(4) + T_parent_joint[:, :3, :3] = joint_rotmats + T_parent_joint[0, :3, 3] = j_tpose[0] + T_parent_joint[1:, :3, 3] = j_tpose[1:] - j_tpose[self.parent_idx[1:]] + + # Forward kinematics. + T_world_joint = T_parent_joint.copy() + for i in range(1, self.num_joints): + T_world_joint[i] = T_world_joint[self.parent_idx[i]] @ T_parent_joint[i] + + # Linear blend skinning. + pose_delta = (joint_rotmats[1:, ...] - np.eye(3)).flatten() + v_blend = v_tpose + np.einsum("byn,n->by", self._posedirs, pose_delta) + v_delta = np.ones((v_blend.shape[0], self.num_joints, 4)) + v_delta[:, :, :3] = v_blend[:, None, :] - j_tpose[None, :, :] + v_posed = np.einsum( + "jxy,vj,vjy->vx", T_world_joint[:, :3, :], self._weights, v_delta + ) + return SmplOutputs(v_posed, self._faces, T_world_joint, T_parent_joint) + + + def main(model_path: Path) -> None: + server = viser.ViserServer() + server.scene.set_up_direction("+y") + server.gui.configure_theme(control_layout="collapsible") + + # Main loop. We'll read pose/shape from the GUI elements, compute the mesh, + # and then send the updated mesh in a loop. + model = SmplHelper(model_path) + gui_elements = make_gui_elements( + server, + num_betas=model.num_betas, + num_joints=model.num_joints, + parent_idx=model.parent_idx, + ) + while True: + # Do nothing if no change. + time.sleep(0.02) + if not gui_elements.changed: + continue + + gui_elements.changed = False + + # Compute SMPL outputs. + smpl_outputs = model.get_outputs( + betas=np.array([x.value for x in gui_elements.gui_betas]), + joint_rotmats=tf.SO3.exp( + # (num_joints, 3) + np.array([x.value for x in gui_elements.gui_joints]) + ).as_matrix(), + ) + server.scene.add_mesh_simple( + "/human", + smpl_outputs.vertices, + smpl_outputs.faces, + wireframe=gui_elements.gui_wireframe.value, + color=gui_elements.gui_rgb.value, + ) + + # Match transform control gizmos to joint positions. + for i, control in enumerate(gui_elements.transform_controls): + control.position = smpl_outputs.T_parent_joint[i, :3, 3] + + + @dataclass + class GuiElements: + """Structure containing handles for reading from GUI elements.""" + + gui_rgb: viser.GuiInputHandle[tuple[int, int, int]] + gui_wireframe: viser.GuiInputHandle[bool] + gui_betas: list[viser.GuiInputHandle[float]] + gui_joints: list[viser.GuiInputHandle[tuple[float, float, float]]] + transform_controls: list[viser.TransformControlsHandle] + + changed: bool + """This flag will be flipped to True whenever the mesh needs to be re-generated.""" + + + def make_gui_elements( + server: viser.ViserServer, + num_betas: int, + num_joints: int, + parent_idx: np.ndarray, + ) -> GuiElements: + """Make GUI elements for interacting with the model.""" + + tab_group = server.gui.add_tab_group() + + def set_changed(_) -> None: + out.changed = True # out is define later! + + # GUI elements: mesh settings + visibility. + with tab_group.add_tab("View", viser.Icon.VIEWFINDER): + gui_rgb = server.gui.add_rgb("Color", initial_value=(90, 200, 255)) + gui_wireframe = server.gui.add_checkbox("Wireframe", initial_value=False) + gui_show_controls = server.gui.add_checkbox("Handles", initial_value=False) + + gui_rgb.on_update(set_changed) + gui_wireframe.on_update(set_changed) + + @gui_show_controls.on_update + def _(_): + for control in transform_controls: + control.visible = gui_show_controls.value + + # GUI elements: shape parameters. + with tab_group.add_tab("Shape", viser.Icon.BOX): + gui_reset_shape = server.gui.add_button("Reset Shape") + gui_random_shape = server.gui.add_button("Random Shape") + + @gui_reset_shape.on_click + def _(_): + for beta in gui_betas: + beta.value = 0.0 + + @gui_random_shape.on_click + def _(_): + for beta in gui_betas: + beta.value = onp.random.normal(loc=0.0, scale=1.0) + + gui_betas = [] + for i in range(num_betas): + beta = server.gui.add_slider( + f"beta{i}", min=-5.0, max=5.0, step=0.01, initial_value=0.0 + ) + gui_betas.append(beta) + beta.on_update(set_changed) + + # GUI elements: joint angles. + with tab_group.add_tab("Joints", viser.Icon.ANGLE): + gui_reset_joints = server.gui.add_button("Reset Joints") + gui_random_joints = server.gui.add_button("Random Joints") + + @gui_reset_joints.on_click + def _(_): + for joint in gui_joints: + joint.value = (0.0, 0.0, 0.0) + + @gui_random_joints.on_click + def _(_): + for joint in gui_joints: + # It's hard to uniformly sample orientations directly in so(3), so we + # first sample on S^3 and then convert. + quat = onp.random.normal(loc=0.0, scale=1.0, size=(4,)) + quat /= onp.linalg.norm(quat) + joint.value = tf.SO3(wxyz=quat).log() + + gui_joints: list[viser.GuiInputHandle[tuple[float, float, float]]] = [] + for i in range(num_joints): + gui_joint = server.gui.add_vector3( + label=f"Joint {i}", + initial_value=(0.0, 0.0, 0.0), + step=0.05, + ) + gui_joints.append(gui_joint) + + def set_callback_in_closure(i: int) -> None: + @gui_joint.on_update + def _(_): + transform_controls[i].wxyz = tf.SO3.exp( + np.array(gui_joints[i].value) + ).wxyz + out.changed = True + + set_callback_in_closure(i) + + # Transform control gizmos on joints. + transform_controls: list[viser.TransformControlsHandle] = [] + prefixed_joint_names = [] # Joint names, but prefixed with parents. + for i in range(num_joints): + prefixed_joint_name = f"joint_{i}" + if i > 0: + prefixed_joint_name = ( + prefixed_joint_names[parent_idx[i]] + "/" + prefixed_joint_name + ) + prefixed_joint_names.append(prefixed_joint_name) + controls = server.scene.add_transform_controls( + f"/smpl/{prefixed_joint_name}", + depth_test=False, + scale=0.2 * (0.75 ** prefixed_joint_name.count("/")), + disable_axes=True, + disable_sliders=True, + visible=gui_show_controls.value, + ) + transform_controls.append(controls) + + def set_callback_in_closure(i: int) -> None: + @controls.on_update + def _(_) -> None: + axisangle = tf.SO3(transform_controls[i].wxyz).log() + gui_joints[i].value = (axisangle[0], axisangle[1], axisangle[2]) + + set_callback_in_closure(i) + + out = GuiElements( + gui_rgb, + gui_wireframe, + gui_betas, + gui_joints, + transform_controls=transform_controls, + changed=True, + ) + return out + + + if __name__ == "__main__": + tyro.cli(main, description=__doc__) diff --git a/versions/0.2.2/_sources/examples/09_urdf_visualizer.rst.txt b/versions/0.2.2/_sources/examples/09_urdf_visualizer.rst.txt new file mode 100644 index 000000000..354dcf8f7 --- /dev/null +++ b/versions/0.2.2/_sources/examples/09_urdf_visualizer.rst.txt @@ -0,0 +1,121 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Robot URDF visualizer +========================================== + + +Requires yourdfpy and robot_descriptions. Any URDF supported by yourdfpy should work. + + +* https://github.com/robot-descriptions/robot_descriptions.py +* https://github.com/clemense/yourdfpy + +The :class:`viser.extras.ViserUrdf` is a lightweight interface between yourdfpy +and viser. It can also take a path to a local URDF file as input. + + + +.. code-block:: python + :linenos: + + + from __future__ import annotations + + import time + + import numpy as onp + import tyro + import viser + from robot_descriptions.loaders.yourdfpy import load_robot_description + from viser.extras import ViserUrdf + + # A subset of robots available in the robot_descriptions package. + ROBOT_MODEL_LIST = ( + "panda_description", + "ur10_description", + "ur3_description", + "ur5_description", + "cassie_description", + "skydio_x2_description", + "allegro_hand_description", + "barrett_hand_description", + "robotiq_2f85_description", + "atlas_drc_description", + "atlas_v4_description", + "draco3_description", + "g1_description", + "h1_description", + "anymal_c_description", + "go2_description", + "mini_cheetah_description", + ) + + + def main() -> None: + # Start viser server. + server = viser.ViserServer() + + # Logic for updating the visualized robot. + gui_joints: list[viser.GuiInputHandle[float]] = [] + initial_angles: list[float] = [] + + def update_robot_model(robot_name: str) -> None: + server.scene.reset() + + loading_modal = server.gui.add_modal("Loading URDF...") + with loading_modal: + server.gui.add_markdown("See terminal for progress!") + + # Create a helper for adding URDFs to Viser. This just adds meshes to the scene, + # helps us set the joint angles, etc. + urdf = ViserUrdf( + server, + # This can also be set to a path to a local URDF file. + urdf_or_path=load_robot_description(robot_name), + ) + loading_modal.close() + + for gui_joint in gui_joints: + gui_joint.remove() + gui_joints.clear() + + for joint_name, (lower, upper) in urdf.get_actuated_joint_limits().items(): + lower = lower if lower is not None else -onp.pi + upper = upper if upper is not None else onp.pi + + initial_angle = 0.0 if lower < 0 and upper > 0 else (lower + upper) / 2.0 + slider = server.gui.add_slider( + label=joint_name, + min=lower, + max=upper, + step=1e-3, + initial_value=initial_angle, + ) + slider.on_update( # When sliders move, we update the URDF configuration. + lambda _: urdf.update_cfg(onp.array([gui.value for gui in gui_joints])) + ) + + gui_joints.append(slider) + initial_angles.append(initial_angle) + + # Apply initial joint angles. + urdf.update_cfg(onp.array([gui.value for gui in gui_joints])) + + robot_model_name = server.gui.add_dropdown("Robot model", ROBOT_MODEL_LIST) + robot_model_name.on_update(lambda _: update_robot_model(robot_model_name.value)) + + # Create joint reset button. + reset_button = server.gui.add_button("Reset") + + @reset_button.on_click + def _(_): + for g, initial_angle in zip(gui_joints, initial_angles): + g.value = initial_angle + + while True: + time.sleep(10.0) + + + if __name__ == "__main__": + tyro.cli(main) diff --git a/versions/0.2.2/_sources/examples/10_realsense.rst.txt b/versions/0.2.2/_sources/examples/10_realsense.rst.txt new file mode 100644 index 000000000..c34596484 --- /dev/null +++ b/versions/0.2.2/_sources/examples/10_realsense.rst.txt @@ -0,0 +1,137 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +RealSense visualizer +========================================== + + +Connect to a RealSense camera, then visualize RGB-D readings as a point clouds. Requires +pyrealsense2. + + + +.. code-block:: python + :linenos: + + + from __future__ import annotations + + import contextlib + + import numpy as np + import numpy.typing as npt + import pyrealsense2 as rs # type: ignore + import viser + from tqdm.auto import tqdm + + + @contextlib.contextmanager + def realsense_pipeline(fps: int = 30): + """Context manager that yields a RealSense pipeline.""" + + # Configure depth and color streams. + pipeline = rs.pipeline() # type: ignore + config = rs.config() # type: ignore + + pipeline_wrapper = rs.pipeline_wrapper(pipeline) # type: ignore + config.resolve(pipeline_wrapper) + + config.enable_stream(rs.stream.depth, rs.format.z16, fps) # type: ignore + config.enable_stream(rs.stream.color, rs.format.rgb8, fps) # type: ignore + + # Start streaming. + pipeline.start(config) + + yield pipeline + + # Close pipeline when done. + pipeline.close() + + + def point_cloud_arrays_from_frames( + depth_frame, color_frame + ) -> tuple[npt.NDArray[np.float32], npt.NDArray[np.uint8]]: + """Maps realsense frames to two arrays. + + Returns: + - A point position array: (N, 3) float32. + - A point color array: (N, 3) uint8. + """ + # Processing blocks. Could be tuned. + point_cloud = rs.pointcloud() # type: ignore + decimate = rs.decimation_filter() # type: ignore + decimate.set_option(rs.option.filter_magnitude, 3) # type: ignore + + # Downsample depth frame. + depth_frame = decimate.process(depth_frame) + + # Map texture and calculate points from frames. Uses frame intrinsics. + point_cloud.map_to(color_frame) + points = point_cloud.calculate(depth_frame) + + # Get color coordinates. + texture_uv = ( + np.asanyarray(points.get_texture_coordinates()) + .view(np.float32) + .reshape((-1, 2)) + ) + color_image = np.asanyarray(color_frame.get_data()) + color_h, color_w, _ = color_image.shape + + # Note: for points that aren't in the view of our RGB camera, we currently clamp to + # the closes available RGB pixel. We could also just remove these points. + texture_uv = texture_uv.clip(0.0, 1.0) + + # Get positions and colors. + positions = np.asanyarray(points.get_vertices()).view(np.float32) + positions = positions.reshape((-1, 3)) + colors = color_image[ + (texture_uv[:, 1] * (color_h - 1.0)).astype(np.int32), + (texture_uv[:, 0] * (color_w - 1.0)).astype(np.int32), + :, + ] + N = positions.shape[0] + + assert positions.shape == (N, 3) + assert positions.dtype == np.float32 + assert colors.shape == (N, 3) + assert colors.dtype == np.uint8 + + return positions, colors + + + def main(): + # Start visualization server. + server = viser.ViserServer() + + with realsense_pipeline() as pipeline: + for i in tqdm(range(10000000)): + # Wait for a coherent pair of frames: depth and color + frames = pipeline.wait_for_frames() + depth_frame = frames.get_depth_frame() + color_frame = frames.get_color_frame() + + # Compute point cloud from frames. + positions, colors = point_cloud_arrays_from_frames(depth_frame, color_frame) + + R = np.array( + [ + [1.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + [0.0, -1.0, 0.0], + ], + dtype=np.float32, + ) + positions = positions @ R.T + + # Visualize. + server.scene.add_point_cloud( + "/realsense", + points=positions * 10.0, + colors=colors, + point_size=0.1, + ) + + + if __name__ == "__main__": + main() diff --git a/versions/0.2.2/_sources/examples/11_colmap_visualizer.rst.txt b/versions/0.2.2/_sources/examples/11_colmap_visualizer.rst.txt new file mode 100644 index 000000000..565687183 --- /dev/null +++ b/versions/0.2.2/_sources/examples/11_colmap_visualizer.rst.txt @@ -0,0 +1,179 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +COLMAP visualizer +========================================== + + +Visualize COLMAP sparse reconstruction outputs. To get demo data, see ``./assets/download_colmap_garden.sh``. + + + +.. code-block:: python + :linenos: + + + import random + import time + from pathlib import Path + + import imageio.v3 as iio + import numpy as onp + import tyro + import viser + import viser.transforms as tf + from tqdm.auto import tqdm + from viser.extras.colmap import ( + read_cameras_binary, + read_images_binary, + read_points3d_binary, + ) + + + def main( + colmap_path: Path = Path(__file__).parent / "assets/colmap_garden/sparse/0", + images_path: Path = Path(__file__).parent / "assets/colmap_garden/images_8", + downsample_factor: int = 2, + ) -> None: + """Visualize COLMAP sparse reconstruction outputs. + + Args: + colmap_path: Path to the COLMAP reconstruction directory. + images_path: Path to the COLMAP images directory. + downsample_factor: Downsample factor for the images. + """ + server = viser.ViserServer() + server.gui.configure_theme(titlebar_content=None, control_layout="collapsible") + + # Load the colmap info. + cameras = read_cameras_binary(colmap_path / "cameras.bin") + images = read_images_binary(colmap_path / "images.bin") + points3d = read_points3d_binary(colmap_path / "points3D.bin") + gui_reset_up = server.gui.add_button( + "Reset up direction", + hint="Set the camera control 'up' direction to the current camera's 'up'.", + ) + + @gui_reset_up.on_click + def _(event: viser.GuiEvent) -> None: + client = event.client + assert client is not None + client.camera.up_direction = tf.SO3(client.camera.wxyz) @ onp.array( + [0.0, -1.0, 0.0] + ) + + gui_points = server.gui.add_slider( + "Max points", + min=1, + max=len(points3d), + step=1, + initial_value=min(len(points3d), 50_000), + ) + gui_frames = server.gui.add_slider( + "Max frames", + min=1, + max=len(images), + step=1, + initial_value=min(len(images), 100), + ) + gui_point_size = server.gui.add_number("Point size", initial_value=0.05) + + def visualize_colmap() -> None: + """Send all COLMAP elements to viser for visualization. This could be optimized + a ton!""" + # Set the point cloud. + points = onp.array([points3d[p_id].xyz for p_id in points3d]) + colors = onp.array([points3d[p_id].rgb for p_id in points3d]) + points_selection = onp.random.choice( + points.shape[0], gui_points.value, replace=False + ) + points = points[points_selection] + colors = colors[points_selection] + + server.scene.add_point_cloud( + name="/colmap/pcd", + points=points, + colors=colors, + point_size=gui_point_size.value, + ) + + # Interpret the images and cameras. + img_ids = [im.id for im in images.values()] + random.shuffle(img_ids) + img_ids = sorted(img_ids[: gui_frames.value]) + + def attach_callback( + frustum: viser.CameraFrustumHandle, frame: viser.FrameHandle + ) -> None: + @frustum.on_click + def _(_) -> None: + for client in server.get_clients().values(): + client.camera.wxyz = frame.wxyz + client.camera.position = frame.position + + for img_id in tqdm(img_ids): + img = images[img_id] + cam = cameras[img.camera_id] + + # Skip images that don't exist. + image_filename = images_path / img.name + if not image_filename.exists(): + continue + + T_world_camera = tf.SE3.from_rotation_and_translation( + tf.SO3(img.qvec), img.tvec + ).inverse() + frame = server.scene.add_frame( + f"/colmap/frame_{img_id}", + wxyz=T_world_camera.rotation().wxyz, + position=T_world_camera.translation(), + axes_length=0.1, + axes_radius=0.005, + ) + + # For pinhole cameras, cam.params will be (fx, fy, cx, cy). + if cam.model != "PINHOLE": + print(f"Expected pinhole camera, but got {cam.model}") + + H, W = cam.height, cam.width + fy = cam.params[1] + image = iio.imread(image_filename) + image = image[::downsample_factor, ::downsample_factor] + frustum = server.scene.add_camera_frustum( + f"/colmap/frame_{img_id}/frustum", + fov=2 * onp.arctan2(H / 2, fy), + aspect=W / H, + scale=0.15, + image=image, + ) + attach_callback(frustum, frame) + + need_update = True + + @gui_points.on_update + def _(_) -> None: + nonlocal need_update + need_update = True + + @gui_frames.on_update + def _(_) -> None: + nonlocal need_update + need_update = True + + @gui_point_size.on_update + def _(_) -> None: + nonlocal need_update + need_update = True + + while True: + if need_update: + need_update = False + + server.scene.reset() + visualize_colmap() + + time.sleep(1e-3) + + + if __name__ == "__main__": + tyro.cli(main) diff --git a/versions/0.2.2/_sources/examples/12_click_meshes.rst.txt b/versions/0.2.2/_sources/examples/12_click_meshes.rst.txt new file mode 100644 index 000000000..621625a27 --- /dev/null +++ b/versions/0.2.2/_sources/examples/12_click_meshes.rst.txt @@ -0,0 +1,93 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Mesh click events +========================================== + + +Click on meshes to select them. The index of the last clicked mesh is displayed in the GUI. + + + +.. code-block:: python + :linenos: + + + import time + + import matplotlib + import viser + + + def main() -> None: + grid_shape = (4, 5) + server = viser.ViserServer() + + with server.gui.add_folder("Last clicked"): + x_value = server.gui.add_number( + label="x", + initial_value=0, + disabled=True, + hint="x coordinate of the last clicked mesh", + ) + y_value = server.gui.add_number( + label="y", + initial_value=0, + disabled=True, + hint="y coordinate of the last clicked mesh", + ) + + def add_swappable_mesh(i: int, j: int) -> None: + """Simple callback that swaps between: + - a gray box + - a colored box + - a colored sphere + + Color is chosen based on the position (i, j) of the mesh in the grid. + """ + + colormap = matplotlib.colormaps["tab20"] + + def create_mesh(counter: int) -> None: + if counter == 0: + color = (0.8, 0.8, 0.8) + else: + index = (i * grid_shape[1] + j) / (grid_shape[0] * grid_shape[1]) + color = colormap(index)[:3] + + if counter in (0, 1): + handle = server.scene.add_box( + name=f"/sphere_{i}_{j}", + position=(i, j, 0.0), + color=color, + dimensions=(0.5, 0.5, 0.5), + ) + else: + handle = server.scene.add_icosphere( + name=f"/sphere_{i}_{j}", + radius=0.4, + color=color, + position=(i, j, 0.0), + ) + + @handle.on_click + def _(_) -> None: + x_value.value = i + y_value.value = j + + # The new mesh will replace the old one because the names + # /sphere_{i}_{j} are the same. + create_mesh((counter + 1) % 3) + + create_mesh(0) + + for i in range(grid_shape[0]): + for j in range(grid_shape[1]): + add_swappable_mesh(i, j) + + while True: + time.sleep(10.0) + + + if __name__ == "__main__": + main() diff --git a/versions/0.2.2/_sources/examples/13_theming.rst.txt b/versions/0.2.2/_sources/examples/13_theming.rst.txt new file mode 100644 index 000000000..38305ab90 --- /dev/null +++ b/versions/0.2.2/_sources/examples/13_theming.rst.txt @@ -0,0 +1,104 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Theming +========================================== + + +Viser includes support for light theming. + + + +.. code-block:: python + :linenos: + + + import time + + import viser + from viser.theme import TitlebarButton, TitlebarConfig, TitlebarImage + + + def main(): + server = viser.ViserServer(label="Viser Theming") + + buttons = ( + TitlebarButton( + text="Getting Started", + icon=None, + href="https://nerf.studio", + ), + TitlebarButton( + text="Github", + icon="GitHub", + href="https://github.com/nerfstudio-project/nerfstudio", + ), + TitlebarButton( + text="Documentation", + icon="Description", + href="https://docs.nerf.studio", + ), + ) + image = TitlebarImage( + image_url_light="https://docs.nerf.studio/_static/imgs/logo.png", + image_url_dark="https://docs.nerf.studio/_static/imgs/logo-dark.png", + image_alt="NerfStudio Logo", + href="https://docs.nerf.studio/", + ) + titlebar_theme = TitlebarConfig(buttons=buttons, image=image) + + server.gui.add_markdown( + "Viser includes support for light theming via the `.configure_theme()` method." + ) + + gui_theme_code = server.gui.add_markdown("no theme applied yet") + + # GUI elements for controllable values. + titlebar = server.gui.add_checkbox("Titlebar", initial_value=True) + dark_mode = server.gui.add_checkbox("Dark mode", initial_value=True) + show_logo = server.gui.add_checkbox("Show logo", initial_value=True) + show_share_button = server.gui.add_checkbox("Show share button", initial_value=True) + brand_color = server.gui.add_rgb("Brand color", (230, 180, 30)) + control_layout = server.gui.add_dropdown( + "Control layout", ("floating", "fixed", "collapsible") + ) + control_width = server.gui.add_dropdown( + "Control width", ("small", "medium", "large"), initial_value="medium" + ) + synchronize = server.gui.add_button("Apply theme", icon=viser.Icon.CHECK) + + def synchronize_theme() -> None: + server.gui.configure_theme( + titlebar_content=titlebar_theme if titlebar.value else None, + control_layout=control_layout.value, + control_width=control_width.value, + dark_mode=dark_mode.value, + show_logo=show_logo.value, + show_share_button=show_share_button.value, + brand_color=brand_color.value, + ) + gui_theme_code.content = f""" + ### Current applied theme + ``` + server.gui.configure_theme( + titlebar_content={"titlebar_content" if titlebar.value else None}, + control_layout="{control_layout.value}", + control_width="{control_width.value}", + dark_mode={dark_mode.value}, + show_logo={show_logo.value}, + show_share_button={show_share_button.value}, + brand_color={brand_color.value}, + ) + ``` + """ + + synchronize.on_click(lambda _: synchronize_theme()) + synchronize_theme() + + while True: + time.sleep(10.0) + + + # main() + if __name__ == "__main__": + main() diff --git a/versions/0.2.2/_sources/examples/14_markdown.rst.txt b/versions/0.2.2/_sources/examples/14_markdown.rst.txt new file mode 100644 index 000000000..de5df24da --- /dev/null +++ b/versions/0.2.2/_sources/examples/14_markdown.rst.txt @@ -0,0 +1,52 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Markdown Demonstration +========================================== + + +Viser GUI has MDX 2 support. + + + +.. code-block:: python + :linenos: + + + import time + from pathlib import Path + + import viser + + server = viser.ViserServer() + server.scene.world_axes.visible = True + + markdown_counter = server.gui.add_markdown("Counter: 0") + + here = Path(__file__).absolute().parent + + button = server.gui.add_button("Remove blurb") + checkbox = server.gui.add_checkbox("Visibility", initial_value=True) + + markdown_source = (here / "./assets/mdx_example.mdx").read_text() + markdown_blurb = server.gui.add_markdown( + content=markdown_source, + image_root=here, + ) + + + @button.on_click + def _(_): + markdown_blurb.remove() + + + @checkbox.on_update + def _(_): + markdown_blurb.visible = checkbox.value + + + counter = 0 + while True: + markdown_counter.content = f"Counter: {counter}" + counter += 1 + time.sleep(0.1) diff --git a/versions/0.2.2/_sources/examples/15_gui_in_scene.rst.txt b/versions/0.2.2/_sources/examples/15_gui_in_scene.rst.txt new file mode 100644 index 000000000..f3eb91bf4 --- /dev/null +++ b/versions/0.2.2/_sources/examples/15_gui_in_scene.rst.txt @@ -0,0 +1,111 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +3D GUI Elements +========================================== + + +``add_3d_gui_container()`` allows standard GUI elements to be incorporated directly into a +3D scene. In this example, we click on coordinate frames to show actions that can be +performed on them. + + + +.. code-block:: python + :linenos: + + + import time + from typing import Optional + + import numpy as onp + import viser + import viser.transforms as tf + + server = viser.ViserServer() + num_frames = 20 + + + @server.on_client_connect + def _(client: viser.ClientHandle) -> None: + """For each client that connects, we create a set of random frames + a click handler for each frame. + + When a frame is clicked, we display a 3D gui node. + """ + + rng = onp.random.default_rng(0) + + displayed_3d_container: Optional[viser.Gui3dContainerHandle] = None + + def make_frame(i: int) -> None: + # Sample a random orientation + position. + wxyz = rng.normal(size=4) + wxyz /= onp.linalg.norm(wxyz) + position = rng.uniform(-3.0, 3.0, size=(3,)) + + # Create a coordinate frame and label. + frame = client.scene.add_frame(f"/frame_{i}", wxyz=wxyz, position=position) + + # Move the camera when we click a frame. + @frame.on_click + def _(_): + nonlocal displayed_3d_container + + # Close previously opened GUI. + if displayed_3d_container is not None: + displayed_3d_container.remove() + + displayed_3d_container = client.scene.add_3d_gui_container( + f"/frame_{i}/gui" + ) + with displayed_3d_container: + go_to = client.gui.add_button("Go to") + randomize_orientation = client.gui.add_button("Randomize orientation") + close = client.gui.add_button("Close GUI") + + @go_to.on_click + def _(_) -> None: + T_world_current = tf.SE3.from_rotation_and_translation( + tf.SO3(client.camera.wxyz), client.camera.position + ) + T_world_target = tf.SE3.from_rotation_and_translation( + tf.SO3(frame.wxyz), frame.position + ) @ tf.SE3.from_translation(onp.array([0.0, 0.0, -0.5])) + + T_current_target = T_world_current.inverse() @ T_world_target + + for j in range(20): + T_world_set = T_world_current @ tf.SE3.exp( + T_current_target.log() * j / 19.0 + ) + + # Important bit: we atomically set both the orientation and the position + # of the camera. + with client.atomic(): + client.camera.wxyz = T_world_set.rotation().wxyz + client.camera.position = T_world_set.translation() + time.sleep(1.0 / 60.0) + + # Mouse interactions should orbit around the frame origin. + client.camera.look_at = frame.position + + @randomize_orientation.on_click + def _(_) -> None: + wxyz = rng.normal(size=4) + wxyz /= onp.linalg.norm(wxyz) + frame.wxyz = wxyz + + @close.on_click + def _(_) -> None: + nonlocal displayed_3d_container + if displayed_3d_container is None: + return + displayed_3d_container.remove() + displayed_3d_container = None + + for i in range(num_frames): + make_frame(i) + + + while True: + time.sleep(1.0) diff --git a/versions/0.2.2/_sources/examples/16_modal.rst.txt b/versions/0.2.2/_sources/examples/16_modal.rst.txt new file mode 100644 index 000000000..08b6013b3 --- /dev/null +++ b/versions/0.2.2/_sources/examples/16_modal.rst.txt @@ -0,0 +1,49 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Modal basics +========================================== + + +Examples of using modals in Viser. + + + +.. code-block:: python + :linenos: + + + import time + + import viser + + + def main(): + server = viser.ViserServer() + + @server.on_client_connect + def _(client: viser.ClientHandle) -> None: + with client.gui.add_modal("Modal example"): + client.gui.add_markdown( + "**The input below determines the title of the modal...**" + ) + + gui_title = client.gui.add_text( + "Title", + initial_value="My Modal", + ) + + modal_button = client.gui.add_button("Show more modals") + + @modal_button.on_click + def _(_) -> None: + with client.gui.add_modal(gui_title.value) as modal: + client.gui.add_markdown("This is content inside the modal!") + client.gui.add_button("Close").on_click(lambda _: modal.close()) + + while True: + time.sleep(0.15) + + + if __name__ == "__main__": + main() diff --git a/versions/0.2.2/_sources/examples/17_background_composite.rst.txt b/versions/0.2.2/_sources/examples/17_background_composite.rst.txt new file mode 100644 index 000000000..dec747943 --- /dev/null +++ b/versions/0.2.2/_sources/examples/17_background_composite.rst.txt @@ -0,0 +1,44 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Depth compositing +========================================== + + +In this example, we show how to use a background image with depth compositing. This can +be useful when we want a 2D image to occlude 3D geometry, such as for NeRF rendering. + + + +.. code-block:: python + :linenos: + + + import time + + import numpy as onp + import trimesh + import trimesh.creation + import viser + + server = viser.ViserServer() + + + img = onp.random.randint(0, 255, size=(1000, 1000, 3), dtype=onp.uint8) + depth = onp.ones((1000, 1000, 1), dtype=onp.float32) + + # Make a square middle portal. + depth[250:750, 250:750, :] = 10.0 + img[250:750, 250:750, :] = 255 + + mesh = trimesh.creation.box((0.5, 0.5, 0.5)) + server.scene.add_mesh_trimesh( + name="/cube", + mesh=mesh, + position=(0, 0, 0.0), + ) + server.scene.set_background_image(img, depth=depth) + + + while True: + time.sleep(1.0) diff --git a/versions/0.2.2/_sources/examples/18_splines.rst.txt b/versions/0.2.2/_sources/examples/18_splines.rst.txt new file mode 100644 index 000000000..74d93b34a --- /dev/null +++ b/versions/0.2.2/_sources/examples/18_splines.rst.txt @@ -0,0 +1,50 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Splines +========================================== + + +Make a ball with some random splines. + + + +.. code-block:: python + :linenos: + + + import time + + import numpy as onp + import viser + + + def main() -> None: + server = viser.ViserServer() + for i in range(10): + positions = onp.random.normal(size=(30, 3)) * 3.0 + server.scene.add_spline_catmull_rom( + f"/catmull_{i}", + positions, + tension=0.5, + line_width=3.0, + color=onp.random.uniform(size=3), + segments=100, + ) + + control_points = onp.random.normal(size=(30 * 2 - 2, 3)) * 3.0 + server.scene.add_spline_cubic_bezier( + f"/cubic_bezier_{i}", + positions, + control_points, + line_width=3.0, + color=onp.random.uniform(size=3), + segments=100, + ) + + while True: + time.sleep(10.0) + + + if __name__ == "__main__": + main() diff --git a/versions/0.2.2/_sources/examples/19_get_renders.rst.txt b/versions/0.2.2/_sources/examples/19_get_renders.rst.txt new file mode 100644 index 000000000..a2be93291 --- /dev/null +++ b/versions/0.2.2/_sources/examples/19_get_renders.rst.txt @@ -0,0 +1,59 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Get Renders +========================================== + + +Example for getting renders from a client's viewport to the Python API. + + + +.. code-block:: python + :linenos: + + + import time + + import imageio.v3 as iio + import numpy as onp + import viser + + + def main(): + server = viser.ViserServer() + + button = server.gui.add_button("Render a GIF") + + @button.on_click + def _(event: viser.GuiEvent) -> None: + client = event.client + assert client is not None + + client.scene.reset() + + images = [] + + for i in range(20): + positions = onp.random.normal(size=(30, 3)) * 3.0 + client.scene.add_spline_catmull_rom( + f"/catmull_{i}", + positions, + tension=0.5, + line_width=3.0, + color=onp.random.uniform(size=3), + ) + images.append(client.camera.get_render(height=720, width=1280)) + + print("Generating and sending GIF...") + client.send_file_download( + "image.gif", iio.imwrite("", images, extension=".gif") + ) + print("Done!") + + while True: + time.sleep(10.0) + + + if __name__ == "__main__": + main() diff --git a/versions/0.2.2/_sources/examples/20_scene_pointer.rst.txt b/versions/0.2.2/_sources/examples/20_scene_pointer.rst.txt new file mode 100644 index 000000000..46a029fdf --- /dev/null +++ b/versions/0.2.2/_sources/examples/20_scene_pointer.rst.txt @@ -0,0 +1,171 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Scene pointer events. +========================================== + + +This example shows how to use scene pointer events to specify rays, and how they can be +used to interact with the scene (e.g., ray-mesh intersections). + +To get the demo data, see ``./assets/download_dragon_mesh.sh``. + + + +.. code-block:: python + :linenos: + + + from __future__ import annotations + + import time + from pathlib import Path + from typing import cast + + import numpy as onp + import trimesh + import trimesh.creation + import trimesh.ray + import viser + import viser.transforms as tf + + server = viser.ViserServer() + server.gui.configure_theme(brand_color=(130, 0, 150)) + server.scene.set_up_direction("+y") + + mesh = cast( + trimesh.Trimesh, trimesh.load_mesh(str(Path(__file__).parent / "assets/dragon.obj")) + ) + mesh.apply_scale(0.05) + + mesh_handle = server.scene.add_mesh_trimesh( + name="/mesh", + mesh=mesh, + position=(0.0, 0.0, 0.0), + ) + + hit_pos_handles: list[viser.GlbHandle] = [] + + + # Buttons + callbacks will operate on a per-client basis, but will modify the global scene! :) + @server.on_client_connect + def _(client: viser.ClientHandle) -> None: + # Set up the camera -- this gives a nice view of the full mesh. + client.camera.position = onp.array([0.0, 0.0, -10.0]) + client.camera.wxyz = onp.array([0.0, 0.0, 0.0, 1.0]) + + # Tests "click" scenepointerevent. + click_button_handle = client.gui.add_button("Add sphere", icon=viser.Icon.POINTER) + + @click_button_handle.on_click + def _(_): + click_button_handle.disabled = True + + @client.scene.on_pointer_event(event_type="click") + def _(event: viser.ScenePointerEvent) -> None: + # Check for intersection with the mesh, using trimesh's ray-mesh intersection. + # Note that mesh is in the mesh frame, so we need to transform the ray. + R_world_mesh = tf.SO3(mesh_handle.wxyz) + R_mesh_world = R_world_mesh.inverse() + origin = (R_mesh_world @ onp.array(event.ray_origin)).reshape(1, 3) + direction = (R_mesh_world @ onp.array(event.ray_direction)).reshape(1, 3) + intersector = trimesh.ray.ray_triangle.RayMeshIntersector(mesh) + hit_pos, _, _ = intersector.intersects_location(origin, direction) + + if len(hit_pos) == 0: + return + client.scene.remove_pointer_callback() + + # Get the first hit position (based on distance from the ray origin). + hit_pos = min(hit_pos, key=lambda x: onp.linalg.norm(x - origin)) + + # Create a sphere at the hit location. + hit_pos_mesh = trimesh.creation.icosphere(radius=0.1) + hit_pos_mesh.vertices += R_world_mesh @ hit_pos + hit_pos_mesh.visual.vertex_colors = (0.5, 0.0, 0.7, 1.0) # type: ignore + hit_pos_handle = server.scene.add_mesh_trimesh( + name=f"/hit_pos_{len(hit_pos_handles)}", mesh=hit_pos_mesh + ) + hit_pos_handles.append(hit_pos_handle) + + @client.scene.on_pointer_callback_removed + def _(): + click_button_handle.disabled = False + + # Tests "rect-select" scenepointerevent. + paint_button_handle = client.gui.add_button("Paint mesh", icon=viser.Icon.PAINT) + + @paint_button_handle.on_click + def _(_): + paint_button_handle.disabled = True + + @client.scene.on_pointer_event(event_type="rect-select") + def _(message: viser.ScenePointerEvent) -> None: + client.scene.remove_pointer_callback() + + global mesh_handle + camera = message.client.camera + + # Put the mesh in the camera frame. + R_world_mesh = tf.SO3(mesh_handle.wxyz) + R_mesh_world = R_world_mesh.inverse() + R_camera_world = tf.SE3.from_rotation_and_translation( + tf.SO3(camera.wxyz), camera.position + ).inverse() + vertices = cast(onp.ndarray, mesh.vertices) + vertices = (R_mesh_world.as_matrix() @ vertices.T).T + vertices = ( + R_camera_world.as_matrix() + @ onp.hstack([vertices, onp.ones((vertices.shape[0], 1))]).T + ).T[:, :3] + + # Get the camera intrinsics, and project the vertices onto the image plane. + fov, aspect = camera.fov, camera.aspect + vertices_proj = vertices[:, :2] / vertices[:, 2].reshape(-1, 1) + vertices_proj /= onp.tan(fov / 2) + vertices_proj[:, 0] /= aspect + + # Move the origin to the upper-left corner, and scale to [0, 1]. + # ... make sure to match the OpenCV's image coordinates! + vertices_proj = (1 + vertices_proj) / 2 + + # Select the vertices that lie inside the 2D selected box, once projected. + mask = ( + (vertices_proj > onp.array(message.screen_pos[0])) + & (vertices_proj < onp.array(message.screen_pos[1])) + ).all(axis=1)[..., None] + + # Update the mesh color based on whether the vertices are inside the box + mesh.visual.vertex_colors = onp.where( # type: ignore + mask, (0.5, 0.0, 0.7, 1.0), (0.9, 0.9, 0.9, 1.0) + ) + mesh_handle = server.scene.add_mesh_trimesh( + name="/mesh", + mesh=mesh, + position=(0.0, 0.0, 0.0), + ) + + @client.scene.on_pointer_callback_removed + def _(): + paint_button_handle.disabled = False + + # Button to clear spheres. + clear_button_handle = client.gui.add_button("Clear scene", icon=viser.Icon.X) + + @clear_button_handle.on_click + def _(_): + """Reset the mesh color and remove all click-generated spheres.""" + global mesh_handle + for handle in hit_pos_handles: + handle.remove() + hit_pos_handles.clear() + mesh.visual.vertex_colors = (0.9, 0.9, 0.9, 1.0) # type: ignore + mesh_handle = server.scene.add_mesh_trimesh( + name="/mesh", + mesh=mesh, + position=(0.0, 0.0, 0.0), + ) + + + while True: + time.sleep(10.0) diff --git a/versions/0.2.2/_sources/examples/21_set_up_direction.rst.txt b/versions/0.2.2/_sources/examples/21_set_up_direction.rst.txt new file mode 100644 index 000000000..5669f90fd --- /dev/null +++ b/versions/0.2.2/_sources/examples/21_set_up_direction.rst.txt @@ -0,0 +1,39 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Set Up Direction +========================================== + + +``.set_up_direction()`` can help us set the global up direction. + + + +.. code-block:: python + :linenos: + + + import time + + import viser + + + def main() -> None: + server = viser.ViserServer() + server.scene.world_axes.visible = True + gui_up = server.gui.add_vector3( + "Up Direction", + initial_value=(0.0, 0.0, 1.0), + step=0.01, + ) + + @gui_up.on_update + def _(_) -> None: + server.scene.set_up_direction(gui_up.value) + + while True: + time.sleep(1.0) + + + if __name__ == "__main__": + main() diff --git a/versions/0.2.2/_sources/examples/22_games.rst.txt b/versions/0.2.2/_sources/examples/22_games.rst.txt new file mode 100644 index 000000000..4c630179a --- /dev/null +++ b/versions/0.2.2/_sources/examples/22_games.rst.txt @@ -0,0 +1,163 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Games +========================================== + + +Some two-player games implemented using scene click events. + + + +.. code-block:: python + :linenos: + + + import time + from typing import Literal + + import numpy as onp + import trimesh.creation + import viser + import viser.transforms as tf + from typing_extensions import assert_never + + + def main() -> None: + server = viser.ViserServer() + server.gui.configure_theme(dark_mode=True) + play_connect_4(server) + + server.gui.add_button("Tic-Tac-Toe").on_click(lambda _: play_tic_tac_toe(server)) + server.gui.add_button("Connect 4").on_click(lambda _: play_connect_4(server)) + + while True: + time.sleep(10.0) + + + def play_connect_4(server: viser.ViserServer) -> None: + """Play a game of Connect 4.""" + server.scene.reset() + + num_rows = 6 + num_cols = 7 + + whose_turn: Literal["red", "yellow"] = "red" + pieces_in_col = [0] * num_cols + + # Create the board frame. + for col in range(num_cols): + for row in range(num_rows): + server.scene.add_mesh_trimesh( + f"/structure/{row}_{col}", + trimesh.creation.annulus(0.45, 0.55, 0.125), + position=(0.0, col, row), + wxyz=tf.SO3.from_y_radians(onp.pi / 2.0).wxyz, + ) + + # Create a sphere to click on for each column. + def setup_column(col: int) -> None: + sphere = server.scene.add_icosphere( + f"/spheres/{col}", + radius=0.25, + position=(0, col, num_rows - 0.25), + color=(255, 255, 255), + ) + + # Drop piece into the column. + @sphere.on_click + def _(_) -> None: + nonlocal whose_turn + whose_turn = "red" if whose_turn != "red" else "yellow" + + row = pieces_in_col[col] + if row == num_rows - 1: + sphere.remove() + + pieces_in_col[col] += 1 + cylinder = trimesh.creation.cylinder(radius=0.4, height=0.125) + piece = server.scene.add_mesh_simple( + f"/game_pieces/{row}_{col}", + cylinder.vertices, + cylinder.faces, + wxyz=tf.SO3.from_y_radians(onp.pi / 2.0).wxyz, + color={"red": (255, 0, 0), "yellow": (255, 255, 0)}[whose_turn], + ) + for row_anim in onp.linspace(num_rows - 1, row, num_rows - row + 1): + piece.position = ( + 0, + col, + row_anim, + ) + time.sleep(1.0 / 30.0) + + for col in range(num_cols): + setup_column(col) + + + def play_tic_tac_toe(server: viser.ViserServer) -> None: + """Play a game of tic-tac-toe.""" + server.scene.reset() + + whose_turn: Literal["x", "o"] = "x" + + for i in range(4): + server.scene.add_spline_catmull_rom( + f"/gridlines/{i}", + ((-0.5, -1.5, 0), (-0.5, 1.5, 0)), + color=(127, 127, 127), + position=(1, 1, 0), + wxyz=tf.SO3.from_z_radians(onp.pi / 2 * i).wxyz, + ) + + def draw_symbol(symbol: Literal["x", "o"], i: int, j: int) -> None: + """Draw an X or O in the given cell.""" + for scale in onp.linspace(0.01, 1.0, 5): + if symbol == "x": + for k in range(2): + server.scene.add_box( + f"/symbols/{i}_{j}/{k}", + dimensions=(0.7 * scale, 0.125 * scale, 0.125), + position=(i, j, 0), + color=(0, 0, 255), + wxyz=tf.SO3.from_z_radians( + onp.pi / 2.0 * k + onp.pi / 4.0 + ).wxyz, + ) + elif symbol == "o": + mesh = trimesh.creation.annulus(0.25 * scale, 0.35 * scale, 0.125) + server.scene.add_mesh_simple( + f"/symbols/{i}_{j}", + mesh.vertices, + mesh.faces, + position=(i, j, 0), + color=(255, 0, 0), + ) + else: + assert_never(symbol) + server.flush() + time.sleep(1.0 / 30.0) + + def setup_cell(i: int, j: int) -> None: + """Create a clickable sphere in a given cell.""" + sphere = server.scene.add_icosphere( + f"/spheres/{i}_{j}", + radius=0.25, + position=(i, j, 0), + color=(255, 255, 255), + ) + + @sphere.on_click + def _(_) -> None: + nonlocal whose_turn + whose_turn = "x" if whose_turn != "x" else "o" + sphere.remove() + draw_symbol(whose_turn, i, j) + + for i in range(3): + for j in range(3): + setup_cell(i, j) + + + if __name__ == "__main__": + main() diff --git a/versions/0.2.2/_sources/examples/23_plotly.rst.txt b/versions/0.2.2/_sources/examples/23_plotly.rst.txt new file mode 100644 index 000000000..f5a596d25 --- /dev/null +++ b/versions/0.2.2/_sources/examples/23_plotly.rst.txt @@ -0,0 +1,83 @@ +.. Comment: this file is automatically generated by `update_example_docs.py`. + It should not be modified manually. + +Plotly. +========================================== + + +Examples of visualizing plotly plots in Viser. + + + +.. code-block:: python + :linenos: + + + import time + + import numpy as onp + import plotly.express as px + import plotly.graph_objects as go + import viser + from PIL import Image + + + def create_sinusoidal_wave(t: float) -> go.Figure: + """Create a sinusoidal wave plot, starting at time t.""" + x_data = onp.linspace(t, t + 6 * onp.pi, 50) + y_data = onp.sin(x_data) * 10 + + fig = px.line( + x=list(x_data), + y=list(y_data), + labels={"x": "x", "y": "sin(x)"}, + title="Sinusoidal Wave", + ) + + # this sets the margins to be tight around the title. + fig.layout.title.automargin = True # type: ignore + fig.update_layout( + margin=dict(l=20, r=20, t=20, b=20), + ) # Reduce plot margins. + + return fig + + + def main() -> None: + server = viser.ViserServer() + + # Plot type 1: Line plot. + line_plot_time = 0.0 + line_plot = server.gui.add_plotly(figure=create_sinusoidal_wave(line_plot_time)) + + # Plot type 2: Image plot. + fig = px.imshow(Image.open("assets/Cal_logo.png")) + fig.update_layout( + margin=dict(l=20, r=20, t=20, b=20), + ) + server.gui.add_plotly(figure=fig, aspect=1.0) + + # Plot type 3: 3D Scatter plot. + fig = px.scatter_3d( + px.data.iris(), + x="sepal_length", + y="sepal_width", + z="petal_width", + color="species", + ) + fig.update_layout(legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01)) + fig.update_layout( + margin=dict(l=20, r=20, t=20, b=20), + ) + server.gui.add_plotly(figure=fig, aspect=1.0) + + while True: + # Update the line plot. + line_plot_time += 0.1 + line_plot.figure = create_sinusoidal_wave(line_plot_time) + + time.sleep(0.01) + + + if __name__ == "__main__": + main() diff --git a/versions/0.2.2/_sources/extras.md.txt b/versions/0.2.2/_sources/extras.md.txt new file mode 100644 index 000000000..ae8920588 --- /dev/null +++ b/versions/0.2.2/_sources/extras.md.txt @@ -0,0 +1,7 @@ +# Record3D + URDF Helpers + + + +.. automodule:: viser.extras + + diff --git a/versions/0.2.2/_sources/gui_api.md.txt b/versions/0.2.2/_sources/gui_api.md.txt new file mode 100644 index 000000000..eb7da09b6 --- /dev/null +++ b/versions/0.2.2/_sources/gui_api.md.txt @@ -0,0 +1,10 @@ +# GUI API + + + +.. autoclass:: viser.GuiApi + :members: + :undoc-members: + :inherited-members: + + diff --git a/versions/0.2.2/_sources/gui_handles.md.txt b/versions/0.2.2/_sources/gui_handles.md.txt new file mode 100644 index 000000000..8d5b3669d --- /dev/null +++ b/versions/0.2.2/_sources/gui_handles.md.txt @@ -0,0 +1,23 @@ +# GUI Handles + + + +.. autoclass:: viser.GuiInputHandle() + +.. autoclass:: viser.GuiButtonHandle() + +.. autoclass:: viser.GuiButtonGroupHandle() + +.. autoclass:: viser.GuiDropdownHandle() + +.. autoclass:: viser.GuiFolderHandle() + +.. autoclass:: viser.GuiMarkdownHandle() + +.. autoclass:: viser.GuiPlotlyHandle() + +.. autoclass:: viser.GuiTabGroupHandle() + +.. autoclass:: viser.GuiTabHandle() + + diff --git a/versions/0.2.2/_sources/icons.md.txt b/versions/0.2.2/_sources/icons.md.txt new file mode 100644 index 000000000..e74595766 --- /dev/null +++ b/versions/0.2.2/_sources/icons.md.txt @@ -0,0 +1,11 @@ +# Icons + +Icons for GUI elements (such as :meth:`GuiApi.add_button()`) can be +specified using the :class:`viser.Icon` enum. + + + +.. autoclass:: viser.IconName +.. autoclass:: viser.Icon + + diff --git a/versions/0.2.2/_sources/index.md.txt b/versions/0.2.2/_sources/index.md.txt new file mode 100644 index 000000000..ddd76a199 --- /dev/null +++ b/versions/0.2.2/_sources/index.md.txt @@ -0,0 +1,108 @@ +# viser + +|mypy| |nbsp| |pyright| |nbsp| |typescript| |nbsp| |versions| + +**viser** is a library for interactive 3D visualization in Python. + +Features include: + +- API for visualizing 3D primitives +- GUI building blocks: buttons, checkboxes, text inputs, sliders, etc. +- Scene interaction tools (clicks, selection, transform gizmos) +- Programmatic camera control and rendering +- An entirely web-based client, for easy use over SSH! + +## Installation + +You can install `viser` with `pip`: + +```bash +pip install viser +``` + +To include example dependencies: + +```bash +pip install viser[examples] +``` + +After an example script is running, you can connect by navigating to the printed +URL (default: `http://localhost:8080`). + +See also: our [development docs](https://viser.studio/development/). + + + +.. toctree:: + :caption: Notes + :hidden: + :maxdepth: 1 + :titlesonly: + + ./conventions.md + ./development.md + +.. toctree:: + :caption: API (Basics) + :hidden: + :maxdepth: 1 + :titlesonly: + + ./server.md + ./scene_api.md + ./gui_api.md + + +.. toctree:: + :caption: API (Advanced) + :hidden: + :maxdepth: 1 + :titlesonly: + + ./client_handles.md + ./camera_handles.md + ./gui_handles.md + ./scene_handles.md + ./events.md + ./icons.md + + +.. toctree:: + :caption: API (Auxiliary) + :hidden: + :maxdepth: 1 + :titlesonly: + + ./transforms.md + ./infrastructure.md + ./extras.md + +.. toctree:: + :caption: Examples + :hidden: + :maxdepth: 1 + :titlesonly: + :glob: + + examples/* + + +.. |build| image:: https://github.com/nerfstudio-project/viser/workflows/build/badge.svg + :alt: Build status icon + :target: https://github.com/nerfstudio-project/viser +.. |mypy| image:: https://github.com/nerfstudio-project/viser/workflows/mypy/badge.svg?branch=main + :alt: Mypy status icon + :target: https://github.com/nerfstudio-project/viser +.. |pyright| image:: https://github.com/nerfstudio-project/viser/workflows/pyright/badge.svg?branch=main + :alt: Mypy status icon + :target: https://github.com/nerfstudio-project/viser +.. |typescript| image:: https://github.com/nerfstudio-project/viser/workflows/typescript-compile/badge.svg + :alt: TypeScript status icon + :target: https://github.com/nerfstudio-project/viser +.. |versions| image:: https://img.shields.io/pypi/pyversions/viser + :alt: Version icon + :target: https://pypi.org/project/viser/ +.. |nbsp| unicode:: 0xA0 + :trim: + + diff --git a/versions/0.2.2/_sources/infrastructure.md.txt b/versions/0.2.2/_sources/infrastructure.md.txt new file mode 100644 index 000000000..a88986cfd --- /dev/null +++ b/versions/0.2.2/_sources/infrastructure.md.txt @@ -0,0 +1,8 @@ +# Communication + + + +.. automodule:: viser.infra + :show-inheritance: + + diff --git a/versions/0.2.2/_sources/scene_api.md.txt b/versions/0.2.2/_sources/scene_api.md.txt new file mode 100644 index 000000000..01bb2de95 --- /dev/null +++ b/versions/0.2.2/_sources/scene_api.md.txt @@ -0,0 +1,10 @@ +# Scene API + + + +.. autoclass:: viser.SceneApi + :members: + :undoc-members: + :inherited-members: + + diff --git a/versions/0.2.2/_sources/scene_handles.md.txt b/versions/0.2.2/_sources/scene_handles.md.txt new file mode 100644 index 000000000..87aa64b5f --- /dev/null +++ b/versions/0.2.2/_sources/scene_handles.md.txt @@ -0,0 +1,35 @@ +# Scene Handles + +A handle is created for each object that is added to the scene. These can be +used to read and set state, as well as detect clicks. + +When a scene node is added to a server (for example, via +:func:`viser.ViserServer.add_frame()`), state is synchronized between all +connected clients. When a scene node is added to a client (for example, via +:func:`viser.ClientHandle.add_frame()`), state is local to a specific client. + + + +.. autoclass:: viser.SceneNodeHandle + +.. autoclass:: viser.CameraFrustumHandle + +.. autoclass:: viser.FrameHandle + +.. autoclass:: viser.BatchedAxesHandle + +.. autoclass:: viser.GlbHandle + +.. autoclass:: viser.Gui3dContainerHandle + +.. autoclass:: viser.ImageHandle + +.. autoclass:: viser.LabelHandle + +.. autoclass:: viser.MeshHandle + +.. autoclass:: viser.PointCloudHandle + +.. autoclass:: viser.TransformControlsHandle + + diff --git a/versions/0.2.2/_sources/server.md.txt b/versions/0.2.2/_sources/server.md.txt new file mode 100644 index 000000000..dc8d7a1a1 --- /dev/null +++ b/versions/0.2.2/_sources/server.md.txt @@ -0,0 +1,7 @@ +# Viser Server + + + +.. autoclass:: viser.ViserServer + + diff --git a/versions/0.2.2/_sources/transforms.md.txt b/versions/0.2.2/_sources/transforms.md.txt new file mode 100644 index 000000000..240bba7e0 --- /dev/null +++ b/versions/0.2.2/_sources/transforms.md.txt @@ -0,0 +1,8 @@ +# Transforms + + + +.. automodule:: viser.transforms + :show-inheritance: + + diff --git a/versions/0.2.2/_static/ansi.css b/versions/0.2.2/_static/ansi.css new file mode 100644 index 000000000..8ef82522a --- /dev/null +++ b/versions/0.2.2/_static/ansi.css @@ -0,0 +1,108 @@ +.ansi-block { + background-color: #ffffff; + color: #000000; +} + +.ansi-bold { + font-weight: bold; +} + +.ansi-dark { + opacity: 0.7; +} + +.ansi-black { + color: #000000; +} + +.ansi-red { + color: #b21717; +} + +.ansi-green { + color: #17b217; +} + +.ansi-yellow { + color: #b26717; +} + +.ansi-blue { + color: #1717b2; +} + +.ansi-magenta { + color: #b217b2; +} + +.ansi-cyan { + color: #17b2b2; +} + +.ansi-white { + color: #b2b2b2; +} + +.ansi-black.ansi-bold { + color: #686868; +} + +.ansi-red.ansi-bold { + color: #ff5454; +} + +.ansi-green.ansi-bold { + color: #54ff54; +} + +.ansi-yellow.ansi-bold { + color: #ffff54; +} + +.ansi-blue.ansi-bold { + color: #5454ff; +} + +.ansi-magenta.ansi-bold { + color: #ff54ff; +} + +.ansi-cyan.ansi-bold { + color: #54ffff; +} + +.ansi-white.ansi-bold { + color: #ffffff; +} + +.ansi-bg_black { + background-color: #000000; +} + +.ansi-bg_red { + background-color: #b21717; +} + +.ansi-bg_green { + background-color: #17b217; +} + +.ansi-bg_yellow { + background-color: #b26717; +} + +.ansi-bg_blue { + background-color: #1717b2; +} + +.ansi-bg_magenta { + background-color: #b217b2; +} + +.ansi-bg_cyan { + background-color: #17b2b2; +} + +.ansi-bg_white { + background-color: #b2b2b2; +} diff --git a/versions/0.2.2/_static/basic.css b/versions/0.2.2/_static/basic.css new file mode 100644 index 000000000..30fee9d0f --- /dev/null +++ b/versions/0.2.2/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/versions/0.2.2/_static/css/custom.css b/versions/0.2.2/_static/css/custom.css new file mode 100644 index 000000000..45a20238f --- /dev/null +++ b/versions/0.2.2/_static/css/custom.css @@ -0,0 +1,4 @@ +img.sidebar-logo { + width: 5em; + margin: 1em 0 0 0; +} diff --git a/versions/0.2.2/_static/debug.css b/versions/0.2.2/_static/debug.css new file mode 100644 index 000000000..74d4aec33 --- /dev/null +++ b/versions/0.2.2/_static/debug.css @@ -0,0 +1,69 @@ +/* + This CSS file should be overridden by the theme authors. It's + meant for debugging and developing the skeleton that this theme provides. +*/ +body { + font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji"; + background: lavender; +} +.sb-announcement { + background: rgb(131, 131, 131); +} +.sb-announcement__inner { + background: black; + color: white; +} +.sb-header { + background: lightskyblue; +} +.sb-header__inner { + background: royalblue; + color: white; +} +.sb-header-secondary { + background: lightcyan; +} +.sb-header-secondary__inner { + background: cornflowerblue; + color: white; +} +.sb-sidebar-primary { + background: lightgreen; +} +.sb-main { + background: blanchedalmond; +} +.sb-main__inner { + background: antiquewhite; +} +.sb-header-article { + background: lightsteelblue; +} +.sb-article-container { + background: snow; +} +.sb-article-main { + background: white; +} +.sb-footer-article { + background: lightpink; +} +.sb-sidebar-secondary { + background: lightgoldenrodyellow; +} +.sb-footer-content { + background: plum; +} +.sb-footer-content__inner { + background: palevioletred; +} +.sb-footer { + background: pink; +} +.sb-footer__inner { + background: salmon; +} +.sb-article { + background: white; +} diff --git a/versions/0.2.2/_static/doctools.js b/versions/0.2.2/_static/doctools.js new file mode 100644 index 000000000..d06a71d75 --- /dev/null +++ b/versions/0.2.2/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/versions/0.2.2/_static/documentation_options.js b/versions/0.2.2/_static/documentation_options.js new file mode 100644 index 000000000..87fc516a4 --- /dev/null +++ b/versions/0.2.2/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'dirhtml', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/versions/0.2.2/_static/file.png b/versions/0.2.2/_static/file.png new file mode 100644 index 000000000..a858a410e Binary files /dev/null and b/versions/0.2.2/_static/file.png differ diff --git a/versions/0.2.2/_static/language_data.js b/versions/0.2.2/_static/language_data.js new file mode 100644 index 000000000..250f5665f --- /dev/null +++ b/versions/0.2.2/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * language_data.js + * ~~~~~~~~~~~~~~~~ + * + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, is available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/versions/0.2.2/_static/logo.svg b/versions/0.2.2/_static/logo.svg new file mode 100644 index 000000000..0ccdbae72 --- /dev/null +++ b/versions/0.2.2/_static/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/versions/0.2.2/_static/minus.png b/versions/0.2.2/_static/minus.png new file mode 100644 index 000000000..d96755fda Binary files /dev/null and b/versions/0.2.2/_static/minus.png differ diff --git a/versions/0.2.2/_static/plus.png b/versions/0.2.2/_static/plus.png new file mode 100644 index 000000000..7107cec93 Binary files /dev/null and b/versions/0.2.2/_static/plus.png differ diff --git a/versions/0.2.2/_static/pygments.css b/versions/0.2.2/_static/pygments.css new file mode 100644 index 000000000..8da4ce698 --- /dev/null +++ b/versions/0.2.2/_static/pygments.css @@ -0,0 +1,259 @@ +.highlight pre { line-height: 125%; } +.highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #49483e } +.highlight { background: #272822; color: #f8f8f2 } +.highlight .c { color: #959077 } /* Comment */ +.highlight .err { color: #ed007e; background-color: #1e0010 } /* Error */ +.highlight .esc { color: #f8f8f2 } /* Escape */ +.highlight .g { color: #f8f8f2 } /* Generic */ +.highlight .k { color: #66d9ef } /* Keyword */ +.highlight .l { color: #ae81ff } /* Literal */ +.highlight .n { color: #f8f8f2 } /* Name */ +.highlight .o { color: #ff4689 } /* Operator */ +.highlight .x { color: #f8f8f2 } /* Other */ +.highlight .p { color: #f8f8f2 } /* Punctuation */ +.highlight .ch { color: #959077 } /* Comment.Hashbang */ +.highlight .cm { color: #959077 } /* Comment.Multiline */ +.highlight .cp { color: #959077 } /* Comment.Preproc */ +.highlight .cpf { color: #959077 } /* Comment.PreprocFile */ +.highlight .c1 { color: #959077 } /* Comment.Single */ +.highlight .cs { color: #959077 } /* Comment.Special */ +.highlight .gd { color: #ff4689 } /* Generic.Deleted */ +.highlight .ge { color: #f8f8f2; font-style: italic } /* Generic.Emph */ +.highlight .ges { color: #f8f8f2; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #f8f8f2 } /* Generic.Error */ +.highlight .gh { color: #f8f8f2 } /* Generic.Heading */ +.highlight .gi { color: #a6e22e } /* Generic.Inserted */ +.highlight .go { color: #66d9ef } /* Generic.Output */ +.highlight .gp { color: #ff4689; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { color: #f8f8f2; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #959077 } /* Generic.Subheading */ +.highlight .gt { color: #f8f8f2 } /* Generic.Traceback */ +.highlight .kc { color: #66d9ef } /* Keyword.Constant */ +.highlight .kd { color: #66d9ef } /* Keyword.Declaration */ +.highlight .kn { color: #ff4689 } /* Keyword.Namespace */ +.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */ +.highlight .kr { color: #66d9ef } /* Keyword.Reserved */ +.highlight .kt { color: #66d9ef } /* Keyword.Type */ +.highlight .ld { color: #e6db74 } /* Literal.Date */ +.highlight .m { color: #ae81ff } /* Literal.Number */ +.highlight .s { color: #e6db74 } /* Literal.String */ +.highlight .na { color: #a6e22e } /* Name.Attribute */ +.highlight .nb { color: #f8f8f2 } /* Name.Builtin */ +.highlight .nc { color: #a6e22e } /* Name.Class */ +.highlight .no { color: #66d9ef } /* Name.Constant */ +.highlight .nd { color: #a6e22e } /* Name.Decorator */ +.highlight .ni { color: #f8f8f2 } /* Name.Entity */ +.highlight .ne { color: #a6e22e } /* Name.Exception */ +.highlight .nf { color: #a6e22e } /* Name.Function */ +.highlight .nl { color: #f8f8f2 } /* Name.Label */ +.highlight .nn { color: #f8f8f2 } /* Name.Namespace */ +.highlight .nx { color: #a6e22e } /* Name.Other */ +.highlight .py { color: #f8f8f2 } /* Name.Property */ +.highlight .nt { color: #ff4689 } /* Name.Tag */ +.highlight .nv { color: #f8f8f2 } /* Name.Variable */ +.highlight .ow { color: #ff4689 } /* Operator.Word */ +.highlight .pm { color: #f8f8f2 } /* Punctuation.Marker */ +.highlight .w { color: #f8f8f2 } /* Text.Whitespace */ +.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */ +.highlight .mf { color: #ae81ff } /* Literal.Number.Float */ +.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ +.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ +.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ +.highlight .sa { color: #e6db74 } /* Literal.String.Affix */ +.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ +.highlight .sc { color: #e6db74 } /* Literal.String.Char */ +.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ +.highlight .sd { color: #e6db74 } /* Literal.String.Doc */ +.highlight .s2 { color: #e6db74 } /* Literal.String.Double */ +.highlight .se { color: #ae81ff } /* Literal.String.Escape */ +.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ +.highlight .si { color: #e6db74 } /* Literal.String.Interpol */ +.highlight .sx { color: #e6db74 } /* Literal.String.Other */ +.highlight .sr { color: #e6db74 } /* Literal.String.Regex */ +.highlight .s1 { color: #e6db74 } /* Literal.String.Single */ +.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ +.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #a6e22e } /* Name.Function.Magic */ +.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ +.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ +.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ +.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ +.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ +@media not print { +body[data-theme="dark"] .highlight pre { line-height: 125%; } +body[data-theme="dark"] .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight .hll { background-color: #404040 } +body[data-theme="dark"] .highlight { background: #202020; color: #d0d0d0 } +body[data-theme="dark"] .highlight .c { color: #ababab; font-style: italic } /* Comment */ +body[data-theme="dark"] .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +body[data-theme="dark"] .highlight .esc { color: #d0d0d0 } /* Escape */ +body[data-theme="dark"] .highlight .g { color: #d0d0d0 } /* Generic */ +body[data-theme="dark"] .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */ +body[data-theme="dark"] .highlight .l { color: #d0d0d0 } /* Literal */ +body[data-theme="dark"] .highlight .n { color: #d0d0d0 } /* Name */ +body[data-theme="dark"] .highlight .o { color: #d0d0d0 } /* Operator */ +body[data-theme="dark"] .highlight .x { color: #d0d0d0 } /* Other */ +body[data-theme="dark"] .highlight .p { color: #d0d0d0 } /* Punctuation */ +body[data-theme="dark"] .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */ +body[data-theme="dark"] .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */ +body[data-theme="dark"] .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */ +body[data-theme="dark"] .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */ +body[data-theme="dark"] .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */ +body[data-theme="dark"] .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ +body[data-theme="dark"] .highlight .gd { color: #ff3a3a } /* Generic.Deleted */ +body[data-theme="dark"] .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ +body[data-theme="dark"] .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +body[data-theme="dark"] .highlight .gr { color: #ff3a3a } /* Generic.Error */ +body[data-theme="dark"] .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ +body[data-theme="dark"] .highlight .gi { color: #589819 } /* Generic.Inserted */ +body[data-theme="dark"] .highlight .go { color: #cccccc } /* Generic.Output */ +body[data-theme="dark"] .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ +body[data-theme="dark"] .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ +body[data-theme="dark"] .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ +body[data-theme="dark"] .highlight .gt { color: #ff3a3a } /* Generic.Traceback */ +body[data-theme="dark"] .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */ +body[data-theme="dark"] .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */ +body[data-theme="dark"] .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */ +body[data-theme="dark"] .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */ +body[data-theme="dark"] .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */ +body[data-theme="dark"] .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */ +body[data-theme="dark"] .highlight .ld { color: #d0d0d0 } /* Literal.Date */ +body[data-theme="dark"] .highlight .m { color: #51b2fd } /* Literal.Number */ +body[data-theme="dark"] .highlight .s { color: #ed9d13 } /* Literal.String */ +body[data-theme="dark"] .highlight .na { color: #bbbbbb } /* Name.Attribute */ +body[data-theme="dark"] .highlight .nb { color: #2fbccd } /* Name.Builtin */ +body[data-theme="dark"] .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */ +body[data-theme="dark"] .highlight .no { color: #40ffff } /* Name.Constant */ +body[data-theme="dark"] .highlight .nd { color: #ffa500 } /* Name.Decorator */ +body[data-theme="dark"] .highlight .ni { color: #d0d0d0 } /* Name.Entity */ +body[data-theme="dark"] .highlight .ne { color: #bbbbbb } /* Name.Exception */ +body[data-theme="dark"] .highlight .nf { color: #71adff } /* Name.Function */ +body[data-theme="dark"] .highlight .nl { color: #d0d0d0 } /* Name.Label */ +body[data-theme="dark"] .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */ +body[data-theme="dark"] .highlight .nx { color: #d0d0d0 } /* Name.Other */ +body[data-theme="dark"] .highlight .py { color: #d0d0d0 } /* Name.Property */ +body[data-theme="dark"] .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */ +body[data-theme="dark"] .highlight .nv { color: #40ffff } /* Name.Variable */ +body[data-theme="dark"] .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */ +body[data-theme="dark"] .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */ +body[data-theme="dark"] .highlight .w { color: #666666 } /* Text.Whitespace */ +body[data-theme="dark"] .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */ +body[data-theme="dark"] .highlight .mf { color: #51b2fd } /* Literal.Number.Float */ +body[data-theme="dark"] .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */ +body[data-theme="dark"] .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */ +body[data-theme="dark"] .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */ +body[data-theme="dark"] .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ +body[data-theme="dark"] .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ +body[data-theme="dark"] .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ +body[data-theme="dark"] .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ +body[data-theme="dark"] .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ +body[data-theme="dark"] .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ +body[data-theme="dark"] .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ +body[data-theme="dark"] .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ +body[data-theme="dark"] .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ +body[data-theme="dark"] .highlight .sx { color: #ffa500 } /* Literal.String.Other */ +body[data-theme="dark"] .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ +body[data-theme="dark"] .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ +body[data-theme="dark"] .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ +body[data-theme="dark"] .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */ +body[data-theme="dark"] .highlight .fm { color: #71adff } /* Name.Function.Magic */ +body[data-theme="dark"] .highlight .vc { color: #40ffff } /* Name.Variable.Class */ +body[data-theme="dark"] .highlight .vg { color: #40ffff } /* Name.Variable.Global */ +body[data-theme="dark"] .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ +body[data-theme="dark"] .highlight .vm { color: #40ffff } /* Name.Variable.Magic */ +body[data-theme="dark"] .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */ +@media (prefers-color-scheme: dark) { +body:not([data-theme="light"]) .highlight pre { line-height: 125%; } +body:not([data-theme="light"]) .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight .hll { background-color: #404040 } +body:not([data-theme="light"]) .highlight { background: #202020; color: #d0d0d0 } +body:not([data-theme="light"]) .highlight .c { color: #ababab; font-style: italic } /* Comment */ +body:not([data-theme="light"]) .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +body:not([data-theme="light"]) .highlight .esc { color: #d0d0d0 } /* Escape */ +body:not([data-theme="light"]) .highlight .g { color: #d0d0d0 } /* Generic */ +body:not([data-theme="light"]) .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */ +body:not([data-theme="light"]) .highlight .l { color: #d0d0d0 } /* Literal */ +body:not([data-theme="light"]) .highlight .n { color: #d0d0d0 } /* Name */ +body:not([data-theme="light"]) .highlight .o { color: #d0d0d0 } /* Operator */ +body:not([data-theme="light"]) .highlight .x { color: #d0d0d0 } /* Other */ +body:not([data-theme="light"]) .highlight .p { color: #d0d0d0 } /* Punctuation */ +body:not([data-theme="light"]) .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */ +body:not([data-theme="light"]) .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */ +body:not([data-theme="light"]) .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */ +body:not([data-theme="light"]) .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */ +body:not([data-theme="light"]) .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */ +body:not([data-theme="light"]) .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ +body:not([data-theme="light"]) .highlight .gd { color: #ff3a3a } /* Generic.Deleted */ +body:not([data-theme="light"]) .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ +body:not([data-theme="light"]) .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +body:not([data-theme="light"]) .highlight .gr { color: #ff3a3a } /* Generic.Error */ +body:not([data-theme="light"]) .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ +body:not([data-theme="light"]) .highlight .gi { color: #589819 } /* Generic.Inserted */ +body:not([data-theme="light"]) .highlight .go { color: #cccccc } /* Generic.Output */ +body:not([data-theme="light"]) .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ +body:not([data-theme="light"]) .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ +body:not([data-theme="light"]) .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ +body:not([data-theme="light"]) .highlight .gt { color: #ff3a3a } /* Generic.Traceback */ +body:not([data-theme="light"]) .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */ +body:not([data-theme="light"]) .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */ +body:not([data-theme="light"]) .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */ +body:not([data-theme="light"]) .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */ +body:not([data-theme="light"]) .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */ +body:not([data-theme="light"]) .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */ +body:not([data-theme="light"]) .highlight .ld { color: #d0d0d0 } /* Literal.Date */ +body:not([data-theme="light"]) .highlight .m { color: #51b2fd } /* Literal.Number */ +body:not([data-theme="light"]) .highlight .s { color: #ed9d13 } /* Literal.String */ +body:not([data-theme="light"]) .highlight .na { color: #bbbbbb } /* Name.Attribute */ +body:not([data-theme="light"]) .highlight .nb { color: #2fbccd } /* Name.Builtin */ +body:not([data-theme="light"]) .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */ +body:not([data-theme="light"]) .highlight .no { color: #40ffff } /* Name.Constant */ +body:not([data-theme="light"]) .highlight .nd { color: #ffa500 } /* Name.Decorator */ +body:not([data-theme="light"]) .highlight .ni { color: #d0d0d0 } /* Name.Entity */ +body:not([data-theme="light"]) .highlight .ne { color: #bbbbbb } /* Name.Exception */ +body:not([data-theme="light"]) .highlight .nf { color: #71adff } /* Name.Function */ +body:not([data-theme="light"]) .highlight .nl { color: #d0d0d0 } /* Name.Label */ +body:not([data-theme="light"]) .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */ +body:not([data-theme="light"]) .highlight .nx { color: #d0d0d0 } /* Name.Other */ +body:not([data-theme="light"]) .highlight .py { color: #d0d0d0 } /* Name.Property */ +body:not([data-theme="light"]) .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */ +body:not([data-theme="light"]) .highlight .nv { color: #40ffff } /* Name.Variable */ +body:not([data-theme="light"]) .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */ +body:not([data-theme="light"]) .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */ +body:not([data-theme="light"]) .highlight .w { color: #666666 } /* Text.Whitespace */ +body:not([data-theme="light"]) .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */ +body:not([data-theme="light"]) .highlight .mf { color: #51b2fd } /* Literal.Number.Float */ +body:not([data-theme="light"]) .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */ +body:not([data-theme="light"]) .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */ +body:not([data-theme="light"]) .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */ +body:not([data-theme="light"]) .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ +body:not([data-theme="light"]) .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ +body:not([data-theme="light"]) .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ +body:not([data-theme="light"]) .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ +body:not([data-theme="light"]) .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ +body:not([data-theme="light"]) .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ +body:not([data-theme="light"]) .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ +body:not([data-theme="light"]) .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ +body:not([data-theme="light"]) .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ +body:not([data-theme="light"]) .highlight .sx { color: #ffa500 } /* Literal.String.Other */ +body:not([data-theme="light"]) .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ +body:not([data-theme="light"]) .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ +body:not([data-theme="light"]) .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ +body:not([data-theme="light"]) .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */ +body:not([data-theme="light"]) .highlight .fm { color: #71adff } /* Name.Function.Magic */ +body:not([data-theme="light"]) .highlight .vc { color: #40ffff } /* Name.Variable.Class */ +body:not([data-theme="light"]) .highlight .vg { color: #40ffff } /* Name.Variable.Global */ +body:not([data-theme="light"]) .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ +body:not([data-theme="light"]) .highlight .vm { color: #40ffff } /* Name.Variable.Magic */ +body:not([data-theme="light"]) .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */ +} +} \ No newline at end of file diff --git a/versions/0.2.2/_static/scripts/furo-extensions.js b/versions/0.2.2/_static/scripts/furo-extensions.js new file mode 100644 index 000000000..e69de29bb diff --git a/versions/0.2.2/_static/scripts/furo.js b/versions/0.2.2/_static/scripts/furo.js new file mode 100644 index 000000000..32e7c05be --- /dev/null +++ b/versions/0.2.2/_static/scripts/furo.js @@ -0,0 +1,3 @@ +/*! For license information please see furo.js.LICENSE.txt */ +(()=>{var t={212:function(t,e,n){var o,r;r=void 0!==n.g?n.g:"undefined"!=typeof window?window:this,o=function(){return function(t){"use strict";var e={navClass:"active",contentClass:"active",nested:!1,nestedClass:"active",offset:0,reflow:!1,events:!0},n=function(t,e,n){if(n.settings.events){var o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n});e.dispatchEvent(o)}},o=function(t){var e=0;if(t.offsetParent)for(;t;)e+=t.offsetTop,t=t.offsetParent;return e>=0?e:0},r=function(t){t&&t.sort((function(t,e){return o(t.content)=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},l=function(t,e){var n=t[t.length-1];if(function(t,e){return!(!s()||!c(t.content,e,!0))}(n,e))return n;for(var o=t.length-1;o>=0;o--)if(c(t[o].content,e))return t[o]},a=function(t,e){if(e.nested&&t.parentNode){var n=t.parentNode.closest("li");n&&(n.classList.remove(e.nestedClass),a(n,e))}},i=function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.remove(e.navClass),t.content.classList.remove(e.contentClass),a(o,e),n("gumshoeDeactivate",o,{link:t.nav,content:t.content,settings:e}))}},u=function(t,e){if(e.nested){var n=t.parentNode.closest("li");n&&(n.classList.add(e.nestedClass),u(n,e))}};return function(o,c){var s,a,d,f,m,v={setup:function(){s=document.querySelectorAll(o),a=[],Array.prototype.forEach.call(s,(function(t){var e=document.getElementById(decodeURIComponent(t.hash.substr(1)));e&&a.push({nav:t,content:e})})),r(a)},detect:function(){var t=l(a,m);t?d&&t.content===d.content||(i(d,m),function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.add(e.navClass),t.content.classList.add(e.contentClass),u(o,e),n("gumshoeActivate",o,{link:t.nav,content:t.content,settings:e}))}}(t,m),d=t):d&&(i(d,m),d=null)}},h=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame(v.detect)},g=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame((function(){r(a),v.detect()}))};return v.destroy=function(){d&&i(d,m),t.removeEventListener("scroll",h,!1),m.reflow&&t.removeEventListener("resize",g,!1),a=null,s=null,d=null,f=null,m=null},m=function(){var t={};return Array.prototype.forEach.call(arguments,(function(e){for(var n in e){if(!e.hasOwnProperty(n))return;t[n]=e[n]}})),t}(e,c||{}),v.setup(),v.detect(),t.addEventListener("scroll",h,!1),m.reflow&&t.addEventListener("resize",g,!1),v}}(r)}.apply(e,[]),void 0===o||(t.exports=o)}},e={};function n(o){var r=e[o];if(void 0!==r)return r.exports;var c=e[o]={exports:{}};return t[o].call(c.exports,c,c.exports,n),c.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var o in e)n.o(e,o)&&!n.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{"use strict";var t=n(212),e=n.n(t),o=null,r=null,c=window.pageYOffset||document.documentElement.scrollTop;const s=64;function l(){const t=localStorage.getItem("theme")||"auto";var e;"light"!==(e=window.matchMedia("(prefers-color-scheme: dark)").matches?"auto"===t?"light":"light"==t?"dark":"auto":"auto"===t?"dark":"dark"==t?"light":"auto")&&"dark"!==e&&"auto"!==e&&(console.error(`Got invalid theme mode: ${e}. Resetting to auto.`),e="auto"),document.body.dataset.theme=e,localStorage.setItem("theme",e),console.log(`Changed to ${e} mode.`)}function a(){!function(){const t=document.getElementsByClassName("theme-toggle");Array.from(t).forEach((t=>{t.addEventListener("click",l)}))}(),function(){let t=0,e=!1;window.addEventListener("scroll",(function(n){t=window.scrollY,e||(window.requestAnimationFrame((function(){var n;n=t,0==Math.floor(r.getBoundingClientRect().top)?r.classList.add("scrolled"):r.classList.remove("scrolled"),function(t){tc&&document.documentElement.classList.remove("show-back-to-top"),c=t}(n),function(t){null!==o&&(0==t?o.scrollTo(0,0):Math.ceil(t)>=Math.floor(document.documentElement.scrollHeight-window.innerHeight)?o.scrollTo(0,o.scrollHeight):document.querySelector(".scroll-current"))}(n),e=!1})),e=!0)})),window.scroll()}(),null!==o&&new(e())(".toc-tree a",{reflow:!0,recursive:!0,navClass:"scroll-current",offset:()=>{let t=parseFloat(getComputedStyle(document.documentElement).fontSize);return r.getBoundingClientRect().height+.5*t+1}})}document.addEventListener("DOMContentLoaded",(function(){document.body.parentNode.classList.remove("no-js"),r=document.querySelector("header"),o=document.querySelector(".toc-scroll"),a()}))})()})(); +//# sourceMappingURL=furo.js.map \ No newline at end of file diff --git a/versions/0.2.2/_static/scripts/furo.js.LICENSE.txt b/versions/0.2.2/_static/scripts/furo.js.LICENSE.txt new file mode 100644 index 000000000..1632189c7 --- /dev/null +++ b/versions/0.2.2/_static/scripts/furo.js.LICENSE.txt @@ -0,0 +1,7 @@ +/*! + * gumshoejs v5.1.2 (patched by @pradyunsg) + * A simple, framework-agnostic scrollspy script. + * (c) 2019 Chris Ferdinandi + * MIT License + * http://github.com/cferdinandi/gumshoe + */ diff --git a/versions/0.2.2/_static/scripts/furo.js.map b/versions/0.2.2/_static/scripts/furo.js.map new file mode 100644 index 000000000..7b7ddb113 --- /dev/null +++ b/versions/0.2.2/_static/scripts/furo.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/furo.js","mappings":";iCAAA,MAQWA,SAWS,IAAX,EAAAC,EACH,EAAAA,EACkB,oBAAXC,OACPA,OACAC,KAbS,EAAF,WACP,OAaJ,SAAUD,GACR,aAMA,IAAIE,EAAW,CAEbC,SAAU,SACVC,aAAc,SAGdC,QAAQ,EACRC,YAAa,SAGbC,OAAQ,EACRC,QAAQ,EAGRC,QAAQ,GA6BNC,EAAY,SAAUC,EAAMC,EAAMC,GAEpC,GAAKA,EAAOC,SAASL,OAArB,CAGA,IAAIM,EAAQ,IAAIC,YAAYL,EAAM,CAChCM,SAAS,EACTC,YAAY,EACZL,OAAQA,IAIVD,EAAKO,cAAcJ,EAVgB,CAWrC,EAOIK,EAAe,SAAUR,GAC3B,IAAIS,EAAW,EACf,GAAIT,EAAKU,aACP,KAAOV,GACLS,GAAYT,EAAKW,UACjBX,EAAOA,EAAKU,aAGhB,OAAOD,GAAY,EAAIA,EAAW,CACpC,EAMIG,EAAe,SAAUC,GACvBA,GACFA,EAASC,MAAK,SAAUC,EAAOC,GAG7B,OAFcR,EAAaO,EAAME,SACnBT,EAAaQ,EAAMC,UACF,EACxB,CACT,GAEJ,EAwCIC,EAAW,SAAUlB,EAAME,EAAUiB,GACvC,IAAIC,EAASpB,EAAKqB,wBACd1B,EAnCU,SAAUO,GAExB,MAA+B,mBAApBA,EAASP,OACX2B,WAAWpB,EAASP,UAItB2B,WAAWpB,EAASP,OAC7B,CA2Be4B,CAAUrB,GACvB,OAAIiB,EAEAK,SAASJ,EAAOD,OAAQ,KACvB/B,EAAOqC,aAAeC,SAASC,gBAAgBC,cAG7CJ,SAASJ,EAAOS,IAAK,KAAOlC,CACrC,EAMImC,EAAa,WACf,OACEC,KAAKC,KAAK5C,EAAOqC,YAAcrC,EAAO6C,cAnCjCF,KAAKG,IACVR,SAASS,KAAKC,aACdV,SAASC,gBAAgBS,aACzBV,SAASS,KAAKE,aACdX,SAASC,gBAAgBU,aACzBX,SAASS,KAAKP,aACdF,SAASC,gBAAgBC,aAkC7B,EAmBIU,EAAY,SAAUzB,EAAUX,GAClC,IAAIqC,EAAO1B,EAASA,EAAS2B,OAAS,GACtC,GAbgB,SAAUC,EAAMvC,GAChC,SAAI4B,MAAgBZ,EAASuB,EAAKxB,QAASf,GAAU,GAEvD,CAUMwC,CAAYH,EAAMrC,GAAW,OAAOqC,EACxC,IAAK,IAAII,EAAI9B,EAAS2B,OAAS,EAAGG,GAAK,EAAGA,IACxC,GAAIzB,EAASL,EAAS8B,GAAG1B,QAASf,GAAW,OAAOW,EAAS8B,EAEjE,EAOIC,EAAmB,SAAUC,EAAK3C,GAEpC,GAAKA,EAAST,QAAWoD,EAAIC,WAA7B,CAGA,IAAIC,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASR,aAG7BkD,EAAiBG,EAAI7C,GAV0B,CAWjD,EAOIiD,EAAa,SAAUC,EAAOlD,GAEhC,GAAKkD,EAAL,CAGA,IAAIL,EAAKK,EAAMP,IAAIG,QAAQ,MACtBD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASX,UAC7B6D,EAAMnC,QAAQgC,UAAUC,OAAOhD,EAASV,cAGxCoD,EAAiBG,EAAI7C,GAGrBJ,EAAU,oBAAqBiD,EAAI,CACjCM,KAAMD,EAAMP,IACZ5B,QAASmC,EAAMnC,QACff,SAAUA,IAjBM,CAmBpB,EAOIoD,EAAiB,SAAUT,EAAK3C,GAElC,GAAKA,EAAST,OAAd,CAGA,IAAIsD,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASR,aAG1B4D,EAAeP,EAAI7C,GAVS,CAW9B,EA6LA,OA1JkB,SAAUsD,EAAUC,GAKpC,IACIC,EAAU7C,EAAU8C,EAASC,EAAS1D,EADtC2D,EAAa,CAUjBA,MAAmB,WAEjBH,EAAWhC,SAASoC,iBAAiBN,GAGrC3C,EAAW,GAGXkD,MAAMC,UAAUC,QAAQC,KAAKR,GAAU,SAAUjB,GAE/C,IAAIxB,EAAUS,SAASyC,eACrBC,mBAAmB3B,EAAK4B,KAAKC,OAAO,KAEjCrD,GAGLJ,EAAS0D,KAAK,CACZ1B,IAAKJ,EACLxB,QAASA,GAEb,IAGAL,EAAaC,EACf,EAKAgD,OAAoB,WAElB,IAAIW,EAASlC,EAAUzB,EAAUX,GAG5BsE,EASDb,GAAWa,EAAOvD,UAAY0C,EAAQ1C,UAG1CkC,EAAWQ,EAASzD,GAzFT,SAAUkD,EAAOlD,GAE9B,GAAKkD,EAAL,CAGA,IAAIL,EAAKK,EAAMP,IAAIG,QAAQ,MACtBD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASX,UAC1B6D,EAAMnC,QAAQgC,UAAUM,IAAIrD,EAASV,cAGrC8D,EAAeP,EAAI7C,GAGnBJ,EAAU,kBAAmBiD,EAAI,CAC/BM,KAAMD,EAAMP,IACZ5B,QAASmC,EAAMnC,QACff,SAAUA,IAjBM,CAmBpB,CAqEIuE,CAASD,EAAQtE,GAGjByD,EAAUa,GAfJb,IACFR,EAAWQ,EAASzD,GACpByD,EAAU,KAchB,GAMIe,EAAgB,SAAUvE,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,sBAAsBf,EAAWgB,OACpD,EAMIC,EAAgB,SAAU3E,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,uBAAsB,WACrChE,EAAaC,GACbgD,EAAWgB,QACb,GACF,EAkDA,OA7CAhB,EAAWkB,QAAU,WAEfpB,GACFR,EAAWQ,EAASzD,GAItBd,EAAO4F,oBAAoB,SAAUN,GAAe,GAChDxE,EAASN,QACXR,EAAO4F,oBAAoB,SAAUF,GAAe,GAItDjE,EAAW,KACX6C,EAAW,KACXC,EAAU,KACVC,EAAU,KACV1D,EAAW,IACb,EAOEA,EA3XS,WACX,IAAI+E,EAAS,CAAC,EAOd,OANAlB,MAAMC,UAAUC,QAAQC,KAAKgB,WAAW,SAAUC,GAChD,IAAK,IAAIC,KAAOD,EAAK,CACnB,IAAKA,EAAIE,eAAeD,GAAM,OAC9BH,EAAOG,GAAOD,EAAIC,EACpB,CACF,IACOH,CACT,CAkXeK,CAAOhG,EAAUmE,GAAW,CAAC,GAGxCI,EAAW0B,QAGX1B,EAAWgB,SAGXzF,EAAOoG,iBAAiB,SAAUd,GAAe,GAC7CxE,EAASN,QACXR,EAAOoG,iBAAiB,SAAUV,GAAe,GAS9CjB,CACT,CAOF,CArcW4B,CAAQvG,EAChB,UAFM,SAEN,uBCXDwG,EAA2B,CAAC,EAGhC,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAaE,QAGrB,IAAIC,EAASN,EAAyBE,GAAY,CAGjDG,QAAS,CAAC,GAOX,OAHAE,EAAoBL,GAAU1B,KAAK8B,EAAOD,QAASC,EAAQA,EAAOD,QAASJ,GAGpEK,EAAOD,OACf,CCrBAJ,EAAoBO,EAAKF,IACxB,IAAIG,EAASH,GAAUA,EAAOI,WAC7B,IAAOJ,EAAiB,QACxB,IAAM,EAEP,OADAL,EAAoBU,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,CAAM,ECLdR,EAAoBU,EAAI,CAACN,EAASQ,KACjC,IAAI,IAAInB,KAAOmB,EACXZ,EAAoBa,EAAED,EAAYnB,KAASO,EAAoBa,EAAET,EAASX,IAC5EqB,OAAOC,eAAeX,EAASX,EAAK,CAAEuB,YAAY,EAAMC,IAAKL,EAAWnB,IAE1E,ECNDO,EAAoBxG,EAAI,WACvB,GAA0B,iBAAf0H,WAAyB,OAAOA,WAC3C,IACC,OAAOxH,MAAQ,IAAIyH,SAAS,cAAb,EAChB,CAAE,MAAOC,GACR,GAAsB,iBAAX3H,OAAqB,OAAOA,MACxC,CACA,CAPuB,GCAxBuG,EAAoBa,EAAI,CAACrB,EAAK6B,IAAUP,OAAOzC,UAAUqB,eAAenB,KAAKiB,EAAK6B,4CCK9EC,EAAY,KACZC,EAAS,KACTC,EAAgB/H,OAAO6C,aAAeP,SAASC,gBAAgByF,UACnE,MAAMC,EAAmB,GA2EzB,SAASC,IACP,MAAMC,EAAeC,aAAaC,QAAQ,UAAY,OAZxD,IAAkBC,EACH,WADGA,EAaItI,OAAOuI,WAAW,gCAAgCC,QAI/C,SAAjBL,EACO,QACgB,SAAhBA,EACA,OAEA,OAIU,SAAjBA,EACO,OACgB,QAAhBA,EACA,QAEA,SA9BoB,SAATG,GAA4B,SAATA,IACzCG,QAAQC,MAAM,2BAA2BJ,yBACzCA,EAAO,QAGThG,SAASS,KAAK4F,QAAQC,MAAQN,EAC9BF,aAAaS,QAAQ,QAASP,GAC9BG,QAAQK,IAAI,cAAcR,UA0B5B,CAkDA,SAASnC,KART,WAEE,MAAM4C,EAAUzG,SAAS0G,uBAAuB,gBAChDrE,MAAMsE,KAAKF,GAASlE,SAASqE,IAC3BA,EAAI9C,iBAAiB,QAAS8B,EAAe,GAEjD,CAGEiB,GA9CF,WAEE,IAAIC,EAA6B,EAC7BC,GAAU,EAEdrJ,OAAOoG,iBAAiB,UAAU,SAAUuB,GAC1CyB,EAA6BpJ,OAAOsJ,QAE/BD,IACHrJ,OAAOwF,uBAAsB,WAzDnC,IAAuB+D,IA0DDH,EA9GkC,GAAlDzG,KAAK6G,MAAM1B,EAAO7F,wBAAwBQ,KAC5CqF,EAAOjE,UAAUM,IAAI,YAErB2D,EAAOjE,UAAUC,OAAO,YAI5B,SAAmCyF,GAC7BA,EAAYtB,EACd3F,SAASC,gBAAgBsB,UAAUC,OAAO,oBAEtCyF,EAAYxB,EACdzF,SAASC,gBAAgBsB,UAAUM,IAAI,oBAC9BoF,EAAYxB,GACrBzF,SAASC,gBAAgBsB,UAAUC,OAAO,oBAG9CiE,EAAgBwB,CAClB,CAoCEE,CAA0BF,GAlC5B,SAA6BA,GACT,OAAd1B,IAKa,GAAb0B,EACF1B,EAAU6B,SAAS,EAAG,GAGtB/G,KAAKC,KAAK2G,IACV5G,KAAK6G,MAAMlH,SAASC,gBAAgBS,aAAehD,OAAOqC,aAE1DwF,EAAU6B,SAAS,EAAG7B,EAAU7E,cAGhBV,SAASqH,cAAc,mBAc3C,CAKEC,CAAoBL,GAwDdF,GAAU,CACZ,IAEAA,GAAU,EAEd,IACArJ,OAAO6J,QACT,CA6BEC,GA1BkB,OAAdjC,GAKJ,IAAI,IAAJ,CAAY,cAAe,CACzBrH,QAAQ,EACRuJ,WAAW,EACX5J,SAAU,iBACVI,OAAQ,KACN,IAAIyJ,EAAM9H,WAAW+H,iBAAiB3H,SAASC,iBAAiB2H,UAChE,OAAOpC,EAAO7F,wBAAwBkI,OAAS,GAAMH,EAAM,CAAC,GAiBlE,CAcA1H,SAAS8D,iBAAiB,oBAT1B,WACE9D,SAASS,KAAKW,WAAWG,UAAUC,OAAO,SAE1CgE,EAASxF,SAASqH,cAAc,UAChC9B,EAAYvF,SAASqH,cAAc,eAEnCxD,GACF","sources":["webpack:///./src/furo/assets/scripts/gumshoe-patched.js","webpack:///webpack/bootstrap","webpack:///webpack/runtime/compat get default export","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/global","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///./src/furo/assets/scripts/furo.js"],"sourcesContent":["/*!\n * gumshoejs v5.1.2 (patched by @pradyunsg)\n * A simple, framework-agnostic scrollspy script.\n * (c) 2019 Chris Ferdinandi\n * MIT License\n * http://github.com/cferdinandi/gumshoe\n */\n\n(function (root, factory) {\n if (typeof define === \"function\" && define.amd) {\n define([], function () {\n return factory(root);\n });\n } else if (typeof exports === \"object\") {\n module.exports = factory(root);\n } else {\n root.Gumshoe = factory(root);\n }\n})(\n typeof global !== \"undefined\"\n ? global\n : typeof window !== \"undefined\"\n ? window\n : this,\n function (window) {\n \"use strict\";\n\n //\n // Defaults\n //\n\n var defaults = {\n // Active classes\n navClass: \"active\",\n contentClass: \"active\",\n\n // Nested navigation\n nested: false,\n nestedClass: \"active\",\n\n // Offset & reflow\n offset: 0,\n reflow: false,\n\n // Event support\n events: true,\n };\n\n //\n // Methods\n //\n\n /**\n * Merge two or more objects together.\n * @param {Object} objects The objects to merge together\n * @returns {Object} Merged values of defaults and options\n */\n var extend = function () {\n var merged = {};\n Array.prototype.forEach.call(arguments, function (obj) {\n for (var key in obj) {\n if (!obj.hasOwnProperty(key)) return;\n merged[key] = obj[key];\n }\n });\n return merged;\n };\n\n /**\n * Emit a custom event\n * @param {String} type The event type\n * @param {Node} elem The element to attach the event to\n * @param {Object} detail Any details to pass along with the event\n */\n var emitEvent = function (type, elem, detail) {\n // Make sure events are enabled\n if (!detail.settings.events) return;\n\n // Create a new event\n var event = new CustomEvent(type, {\n bubbles: true,\n cancelable: true,\n detail: detail,\n });\n\n // Dispatch the event\n elem.dispatchEvent(event);\n };\n\n /**\n * Get an element's distance from the top of the Document.\n * @param {Node} elem The element\n * @return {Number} Distance from the top in pixels\n */\n var getOffsetTop = function (elem) {\n var location = 0;\n if (elem.offsetParent) {\n while (elem) {\n location += elem.offsetTop;\n elem = elem.offsetParent;\n }\n }\n return location >= 0 ? location : 0;\n };\n\n /**\n * Sort content from first to last in the DOM\n * @param {Array} contents The content areas\n */\n var sortContents = function (contents) {\n if (contents) {\n contents.sort(function (item1, item2) {\n var offset1 = getOffsetTop(item1.content);\n var offset2 = getOffsetTop(item2.content);\n if (offset1 < offset2) return -1;\n return 1;\n });\n }\n };\n\n /**\n * Get the offset to use for calculating position\n * @param {Object} settings The settings for this instantiation\n * @return {Float} The number of pixels to offset the calculations\n */\n var getOffset = function (settings) {\n // if the offset is a function run it\n if (typeof settings.offset === \"function\") {\n return parseFloat(settings.offset());\n }\n\n // Otherwise, return it as-is\n return parseFloat(settings.offset);\n };\n\n /**\n * Get the document element's height\n * @private\n * @returns {Number}\n */\n var getDocumentHeight = function () {\n return Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight,\n document.body.offsetHeight,\n document.documentElement.offsetHeight,\n document.body.clientHeight,\n document.documentElement.clientHeight,\n );\n };\n\n /**\n * Determine if an element is in view\n * @param {Node} elem The element\n * @param {Object} settings The settings for this instantiation\n * @param {Boolean} bottom If true, check if element is above bottom of viewport instead\n * @return {Boolean} Returns true if element is in the viewport\n */\n var isInView = function (elem, settings, bottom) {\n var bounds = elem.getBoundingClientRect();\n var offset = getOffset(settings);\n if (bottom) {\n return (\n parseInt(bounds.bottom, 10) <\n (window.innerHeight || document.documentElement.clientHeight)\n );\n }\n return parseInt(bounds.top, 10) <= offset;\n };\n\n /**\n * Check if at the bottom of the viewport\n * @return {Boolean} If true, page is at the bottom of the viewport\n */\n var isAtBottom = function () {\n if (\n Math.ceil(window.innerHeight + window.pageYOffset) >=\n getDocumentHeight()\n )\n return true;\n return false;\n };\n\n /**\n * Check if the last item should be used (even if not at the top of the page)\n * @param {Object} item The last item\n * @param {Object} settings The settings for this instantiation\n * @return {Boolean} If true, use the last item\n */\n var useLastItem = function (item, settings) {\n if (isAtBottom() && isInView(item.content, settings, true)) return true;\n return false;\n };\n\n /**\n * Get the active content\n * @param {Array} contents The content areas\n * @param {Object} settings The settings for this instantiation\n * @return {Object} The content area and matching navigation link\n */\n var getActive = function (contents, settings) {\n var last = contents[contents.length - 1];\n if (useLastItem(last, settings)) return last;\n for (var i = contents.length - 1; i >= 0; i--) {\n if (isInView(contents[i].content, settings)) return contents[i];\n }\n };\n\n /**\n * Deactivate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var deactivateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested || !nav.parentNode) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Remove the active class\n li.classList.remove(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n deactivateNested(li, settings);\n };\n\n /**\n * Deactivate a nav and content area\n * @param {Object} items The nav item and content to deactivate\n * @param {Object} settings The settings for this instantiation\n */\n var deactivate = function (items, settings) {\n // Make sure there are items to deactivate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Remove the active class from the nav and content\n li.classList.remove(settings.navClass);\n items.content.classList.remove(settings.contentClass);\n\n // Deactivate any parent navs in a nested navigation\n deactivateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeDeactivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Activate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var activateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Add the active class\n li.classList.add(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n activateNested(li, settings);\n };\n\n /**\n * Activate a nav and content area\n * @param {Object} items The nav item and content to activate\n * @param {Object} settings The settings for this instantiation\n */\n var activate = function (items, settings) {\n // Make sure there are items to activate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Add the active class to the nav and content\n li.classList.add(settings.navClass);\n items.content.classList.add(settings.contentClass);\n\n // Activate any parent navs in a nested navigation\n activateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeActivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Create the Constructor object\n * @param {String} selector The selector to use for navigation items\n * @param {Object} options User options and settings\n */\n var Constructor = function (selector, options) {\n //\n // Variables\n //\n\n var publicAPIs = {};\n var navItems, contents, current, timeout, settings;\n\n //\n // Methods\n //\n\n /**\n * Set variables from DOM elements\n */\n publicAPIs.setup = function () {\n // Get all nav items\n navItems = document.querySelectorAll(selector);\n\n // Create contents array\n contents = [];\n\n // Loop through each item, get it's matching content, and push to the array\n Array.prototype.forEach.call(navItems, function (item) {\n // Get the content for the nav item\n var content = document.getElementById(\n decodeURIComponent(item.hash.substr(1)),\n );\n if (!content) return;\n\n // Push to the contents array\n contents.push({\n nav: item,\n content: content,\n });\n });\n\n // Sort contents by the order they appear in the DOM\n sortContents(contents);\n };\n\n /**\n * Detect which content is currently active\n */\n publicAPIs.detect = function () {\n // Get the active content\n var active = getActive(contents, settings);\n\n // if there's no active content, deactivate and bail\n if (!active) {\n if (current) {\n deactivate(current, settings);\n current = null;\n }\n return;\n }\n\n // If the active content is the one currently active, do nothing\n if (current && active.content === current.content) return;\n\n // Deactivate the current content and activate the new content\n deactivate(current, settings);\n activate(active, settings);\n\n // Update the currently active content\n current = active;\n };\n\n /**\n * Detect the active content on scroll\n * Debounced for performance\n */\n var scrollHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(publicAPIs.detect);\n };\n\n /**\n * Update content sorting on resize\n * Debounced for performance\n */\n var resizeHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(function () {\n sortContents(contents);\n publicAPIs.detect();\n });\n };\n\n /**\n * Destroy the current instantiation\n */\n publicAPIs.destroy = function () {\n // Undo DOM changes\n if (current) {\n deactivate(current, settings);\n }\n\n // Remove event listeners\n window.removeEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.removeEventListener(\"resize\", resizeHandler, false);\n }\n\n // Reset variables\n contents = null;\n navItems = null;\n current = null;\n timeout = null;\n settings = null;\n };\n\n /**\n * Initialize the current instantiation\n */\n var init = function () {\n // Merge user options into defaults\n settings = extend(defaults, options || {});\n\n // Setup variables based on the current DOM\n publicAPIs.setup();\n\n // Find the currently active content\n publicAPIs.detect();\n\n // Setup event listeners\n window.addEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.addEventListener(\"resize\", resizeHandler, false);\n }\n };\n\n //\n // Initialize and return the public APIs\n //\n\n init();\n return publicAPIs;\n };\n\n //\n // Return the Constructor\n //\n\n return Constructor;\n },\n);\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","import Gumshoe from \"./gumshoe-patched.js\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Scroll Handling\n////////////////////////////////////////////////////////////////////////////////\nvar tocScroll = null;\nvar header = null;\nvar lastScrollTop = window.pageYOffset || document.documentElement.scrollTop;\nconst GO_TO_TOP_OFFSET = 64;\n\nfunction scrollHandlerForHeader() {\n if (Math.floor(header.getBoundingClientRect().top) == 0) {\n header.classList.add(\"scrolled\");\n } else {\n header.classList.remove(\"scrolled\");\n }\n}\n\nfunction scrollHandlerForBackToTop(positionY) {\n if (positionY < GO_TO_TOP_OFFSET) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n } else {\n if (positionY < lastScrollTop) {\n document.documentElement.classList.add(\"show-back-to-top\");\n } else if (positionY > lastScrollTop) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n }\n }\n lastScrollTop = positionY;\n}\n\nfunction scrollHandlerForTOC(positionY) {\n if (tocScroll === null) {\n return;\n }\n\n // top of page.\n if (positionY == 0) {\n tocScroll.scrollTo(0, 0);\n } else if (\n // bottom of page.\n Math.ceil(positionY) >=\n Math.floor(document.documentElement.scrollHeight - window.innerHeight)\n ) {\n tocScroll.scrollTo(0, tocScroll.scrollHeight);\n } else {\n // somewhere in the middle.\n const current = document.querySelector(\".scroll-current\");\n if (current == null) {\n return;\n }\n\n // https://github.com/pypa/pip/issues/9159 This breaks scroll behaviours.\n // // scroll the currently \"active\" heading in toc, into view.\n // const rect = current.getBoundingClientRect();\n // if (0 > rect.top) {\n // current.scrollIntoView(true); // the argument is \"alignTop\"\n // } else if (rect.bottom > window.innerHeight) {\n // current.scrollIntoView(false);\n // }\n }\n}\n\nfunction scrollHandler(positionY) {\n scrollHandlerForHeader();\n scrollHandlerForBackToTop(positionY);\n scrollHandlerForTOC(positionY);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Theme Toggle\n////////////////////////////////////////////////////////////////////////////////\nfunction setTheme(mode) {\n if (mode !== \"light\" && mode !== \"dark\" && mode !== \"auto\") {\n console.error(`Got invalid theme mode: ${mode}. Resetting to auto.`);\n mode = \"auto\";\n }\n\n document.body.dataset.theme = mode;\n localStorage.setItem(\"theme\", mode);\n console.log(`Changed to ${mode} mode.`);\n}\n\nfunction cycleThemeOnce() {\n const currentTheme = localStorage.getItem(\"theme\") || \"auto\";\n const prefersDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n if (prefersDark) {\n // Auto (dark) -> Light -> Dark\n if (currentTheme === \"auto\") {\n setTheme(\"light\");\n } else if (currentTheme == \"light\") {\n setTheme(\"dark\");\n } else {\n setTheme(\"auto\");\n }\n } else {\n // Auto (light) -> Dark -> Light\n if (currentTheme === \"auto\") {\n setTheme(\"dark\");\n } else if (currentTheme == \"dark\") {\n setTheme(\"light\");\n } else {\n setTheme(\"auto\");\n }\n }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Setup\n////////////////////////////////////////////////////////////////////////////////\nfunction setupScrollHandler() {\n // Taken from https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event\n let last_known_scroll_position = 0;\n let ticking = false;\n\n window.addEventListener(\"scroll\", function (e) {\n last_known_scroll_position = window.scrollY;\n\n if (!ticking) {\n window.requestAnimationFrame(function () {\n scrollHandler(last_known_scroll_position);\n ticking = false;\n });\n\n ticking = true;\n }\n });\n window.scroll();\n}\n\nfunction setupScrollSpy() {\n if (tocScroll === null) {\n return;\n }\n\n // Scrollspy -- highlight table on contents, based on scroll\n new Gumshoe(\".toc-tree a\", {\n reflow: true,\n recursive: true,\n navClass: \"scroll-current\",\n offset: () => {\n let rem = parseFloat(getComputedStyle(document.documentElement).fontSize);\n return header.getBoundingClientRect().height + 0.5 * rem + 1;\n },\n });\n}\n\nfunction setupTheme() {\n // Attach event handlers for toggling themes\n const buttons = document.getElementsByClassName(\"theme-toggle\");\n Array.from(buttons).forEach((btn) => {\n btn.addEventListener(\"click\", cycleThemeOnce);\n });\n}\n\nfunction setup() {\n setupTheme();\n setupScrollHandler();\n setupScrollSpy();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entrypoint\n////////////////////////////////////////////////////////////////////////////////\nfunction main() {\n document.body.parentNode.classList.remove(\"no-js\");\n\n header = document.querySelector(\"header\");\n tocScroll = document.querySelector(\".toc-scroll\");\n\n setup();\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", main);\n"],"names":["root","g","window","this","defaults","navClass","contentClass","nested","nestedClass","offset","reflow","events","emitEvent","type","elem","detail","settings","event","CustomEvent","bubbles","cancelable","dispatchEvent","getOffsetTop","location","offsetParent","offsetTop","sortContents","contents","sort","item1","item2","content","isInView","bottom","bounds","getBoundingClientRect","parseFloat","getOffset","parseInt","innerHeight","document","documentElement","clientHeight","top","isAtBottom","Math","ceil","pageYOffset","max","body","scrollHeight","offsetHeight","getActive","last","length","item","useLastItem","i","deactivateNested","nav","parentNode","li","closest","classList","remove","deactivate","items","link","activateNested","add","selector","options","navItems","current","timeout","publicAPIs","querySelectorAll","Array","prototype","forEach","call","getElementById","decodeURIComponent","hash","substr","push","active","activate","scrollHandler","cancelAnimationFrame","requestAnimationFrame","detect","resizeHandler","destroy","removeEventListener","merged","arguments","obj","key","hasOwnProperty","extend","setup","addEventListener","factory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","n","getter","__esModule","d","a","definition","o","Object","defineProperty","enumerable","get","globalThis","Function","e","prop","tocScroll","header","lastScrollTop","scrollTop","GO_TO_TOP_OFFSET","cycleThemeOnce","currentTheme","localStorage","getItem","mode","matchMedia","matches","console","error","dataset","theme","setItem","log","buttons","getElementsByClassName","from","btn","setupTheme","last_known_scroll_position","ticking","scrollY","positionY","floor","scrollHandlerForBackToTop","scrollTo","querySelector","scrollHandlerForTOC","scroll","setupScrollHandler","recursive","rem","getComputedStyle","fontSize","height"],"sourceRoot":""} \ No newline at end of file diff --git a/versions/0.2.2/_static/searchtools.js b/versions/0.2.2/_static/searchtools.js new file mode 100644 index 000000000..7918c3fab --- /dev/null +++ b/versions/0.2.2/_static/searchtools.js @@ -0,0 +1,574 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + `Search finished, found ${resultCount} page(s) matching the search query.` + ); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent !== undefined) return docContent.textContent; + console.warn( + "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + /** + * execute search (requires search index to be loaded) + */ + query: (query) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + // array of [docname, title, anchor, descr, score, filename] + let results = []; + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + results.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id] of foundEntries) { + let score = Math.round(100 * queryLower.length / entry.length) + results.push([ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // lookup as object + objectTerms.forEach((term) => + results.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); + + // now sort the results by score (in opposite order of appearance, since the + // display function below uses pop() to retrieve items) and then + // alphabetically + results.sort((a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; + }); + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + results = results.reverse(); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord) && !terms[word]) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord) && !titleTerms[word]) + arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); + }); + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + else fileMap.set(file, [word]); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords) => { + const text = Search.htmlToText(htmlText); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/versions/0.2.2/_static/skeleton.css b/versions/0.2.2/_static/skeleton.css new file mode 100644 index 000000000..467c878c6 --- /dev/null +++ b/versions/0.2.2/_static/skeleton.css @@ -0,0 +1,296 @@ +/* Some sane resets. */ +html { + height: 100%; +} + +body { + margin: 0; + min-height: 100%; +} + +/* All the flexbox magic! */ +body, +.sb-announcement, +.sb-content, +.sb-main, +.sb-container, +.sb-container__inner, +.sb-article-container, +.sb-footer-content, +.sb-header, +.sb-header-secondary, +.sb-footer { + display: flex; +} + +/* These order things vertically */ +body, +.sb-main, +.sb-article-container { + flex-direction: column; +} + +/* Put elements in the center */ +.sb-header, +.sb-header-secondary, +.sb-container, +.sb-content, +.sb-footer, +.sb-footer-content { + justify-content: center; +} +/* Put elements at the ends */ +.sb-article-container { + justify-content: space-between; +} + +/* These elements grow. */ +.sb-main, +.sb-content, +.sb-container, +article { + flex-grow: 1; +} + +/* Because padding making this wider is not fun */ +article { + box-sizing: border-box; +} + +/* The announcements element should never be wider than the page. */ +.sb-announcement { + max-width: 100%; +} + +.sb-sidebar-primary, +.sb-sidebar-secondary { + flex-shrink: 0; + width: 17rem; +} + +.sb-announcement__inner { + justify-content: center; + + box-sizing: border-box; + height: 3rem; + + overflow-x: auto; + white-space: nowrap; +} + +/* Sidebars, with checkbox-based toggle */ +.sb-sidebar-primary, +.sb-sidebar-secondary { + position: fixed; + height: 100%; + top: 0; +} + +.sb-sidebar-primary { + left: -17rem; + transition: left 250ms ease-in-out; +} +.sb-sidebar-secondary { + right: -17rem; + transition: right 250ms ease-in-out; +} + +.sb-sidebar-toggle { + display: none; +} +.sb-sidebar-overlay { + position: fixed; + top: 0; + width: 0; + height: 0; + + transition: width 0ms ease 250ms, height 0ms ease 250ms, opacity 250ms ease; + + opacity: 0; + background-color: rgba(0, 0, 0, 0.54); +} + +#sb-sidebar-toggle--primary:checked + ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--primary"], +#sb-sidebar-toggle--secondary:checked + ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--secondary"] { + width: 100%; + height: 100%; + opacity: 1; + transition: width 0ms ease, height 0ms ease, opacity 250ms ease; +} + +#sb-sidebar-toggle--primary:checked ~ .sb-container .sb-sidebar-primary { + left: 0; +} +#sb-sidebar-toggle--secondary:checked ~ .sb-container .sb-sidebar-secondary { + right: 0; +} + +/* Full-width mode */ +.drop-secondary-sidebar-for-full-width-content + .hide-when-secondary-sidebar-shown { + display: none !important; +} +.drop-secondary-sidebar-for-full-width-content .sb-sidebar-secondary { + display: none !important; +} + +/* Mobile views */ +.sb-page-width { + width: 100%; +} + +.sb-article-container, +.sb-footer-content__inner, +.drop-secondary-sidebar-for-full-width-content .sb-article, +.drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 100vw; +} + +.sb-article, +.match-content-width { + padding: 0 1rem; + box-sizing: border-box; +} + +@media (min-width: 32rem) { + .sb-article, + .match-content-width { + padding: 0 2rem; + } +} + +/* Tablet views */ +@media (min-width: 42rem) { + .sb-article-container { + width: auto; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 42rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} +@media (min-width: 46rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 46rem; + } + .sb-article, + .match-content-width { + width: 46rem; + } +} +@media (min-width: 50rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 50rem; + } + .sb-article, + .match-content-width { + width: 50rem; + } +} + +/* Tablet views */ +@media (min-width: 59rem) { + .sb-sidebar-secondary { + position: static; + } + .hide-when-secondary-sidebar-shown { + display: none !important; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 59rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} +@media (min-width: 63rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 63rem; + } + .sb-article, + .match-content-width { + width: 46rem; + } +} +@media (min-width: 67rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } + .sb-article, + .match-content-width { + width: 50rem; + } +} + +/* Desktop views */ +@media (min-width: 76rem) { + .sb-sidebar-primary { + position: static; + } + .hide-when-primary-sidebar-shown { + display: none !important; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 59rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} + +/* Full desktop views */ +@media (min-width: 80rem) { + .sb-article, + .match-content-width { + width: 46rem; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 63rem; + } +} + +@media (min-width: 84rem) { + .sb-article, + .match-content-width { + width: 50rem; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } +} + +@media (min-width: 88rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } + .sb-page-width { + width: 88rem; + } +} diff --git a/versions/0.2.2/_static/sphinx_highlight.js b/versions/0.2.2/_static/sphinx_highlight.js new file mode 100644 index 000000000..8a96c69a1 --- /dev/null +++ b/versions/0.2.2/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/versions/0.2.2/_static/styles/furo-extensions.css b/versions/0.2.2/_static/styles/furo-extensions.css new file mode 100644 index 000000000..bc447f228 --- /dev/null +++ b/versions/0.2.2/_static/styles/furo-extensions.css @@ -0,0 +1,2 @@ +#furo-sidebar-ad-placement{padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)}#furo-sidebar-ad-placement .ethical-sidebar{background:var(--color-background-secondary);border:none;box-shadow:none}#furo-sidebar-ad-placement .ethical-sidebar:hover{background:var(--color-background-hover)}#furo-sidebar-ad-placement .ethical-sidebar a{color:var(--color-foreground-primary)}#furo-sidebar-ad-placement .ethical-callout a{color:var(--color-foreground-secondary)!important}#furo-readthedocs-versions{background:transparent;display:block;position:static;width:100%}#furo-readthedocs-versions .rst-versions{background:#1a1c1e}#furo-readthedocs-versions .rst-current-version{background:var(--color-sidebar-item-background);cursor:unset}#furo-readthedocs-versions .rst-current-version:hover{background:var(--color-sidebar-item-background)}#furo-readthedocs-versions .rst-current-version .fa-book{color:var(--color-foreground-primary)}#furo-readthedocs-versions>.rst-other-versions{padding:0}#furo-readthedocs-versions>.rst-other-versions small{opacity:1}#furo-readthedocs-versions .injected .rst-versions{position:unset}#furo-readthedocs-versions:focus-within,#furo-readthedocs-versions:hover{box-shadow:0 0 0 1px var(--color-sidebar-background-border)}#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:hover .rst-current-version{background:#1a1c1e;font-size:inherit;height:auto;line-height:inherit;padding:12px;text-align:right}#furo-readthedocs-versions:focus-within .rst-current-version .fa-book,#furo-readthedocs-versions:hover .rst-current-version .fa-book{color:#fff;float:left}#furo-readthedocs-versions:focus-within .fa-caret-down,#furo-readthedocs-versions:hover .fa-caret-down{display:none}#furo-readthedocs-versions:focus-within .injected,#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:focus-within .rst-other-versions,#furo-readthedocs-versions:hover .injected,#furo-readthedocs-versions:hover .rst-current-version,#furo-readthedocs-versions:hover .rst-other-versions{display:block}#furo-readthedocs-versions:focus-within>.rst-current-version,#furo-readthedocs-versions:hover>.rst-current-version{display:none}.highlight:hover button.copybtn{color:var(--color-code-foreground)}.highlight button.copybtn{align-items:center;background-color:var(--color-code-background);border:none;color:var(--color-background-item);cursor:pointer;height:1.25em;opacity:1;right:.5rem;top:.625rem;transition:color .3s,opacity .3s;width:1.25em}.highlight button.copybtn:hover{background-color:var(--color-code-background);color:var(--color-brand-content)}.highlight button.copybtn:after{background-color:transparent;color:var(--color-code-foreground);display:none}.highlight button.copybtn.success{color:#22863a;transition:color 0ms}.highlight button.copybtn.success:after{display:block}.highlight button.copybtn svg{padding:0}body{--sd-color-primary:var(--color-brand-primary);--sd-color-primary-highlight:var(--color-brand-content);--sd-color-primary-text:var(--color-background-primary);--sd-color-shadow:rgba(0,0,0,.05);--sd-color-card-border:var(--color-card-border);--sd-color-card-border-hover:var(--color-brand-content);--sd-color-card-background:var(--color-card-background);--sd-color-card-text:var(--color-foreground-primary);--sd-color-card-header:var(--color-card-marginals-background);--sd-color-card-footer:var(--color-card-marginals-background);--sd-color-tabs-label-active:var(--color-brand-content);--sd-color-tabs-label-hover:var(--color-foreground-muted);--sd-color-tabs-label-inactive:var(--color-foreground-muted);--sd-color-tabs-underline-active:var(--color-brand-content);--sd-color-tabs-underline-hover:var(--color-foreground-border);--sd-color-tabs-underline-inactive:var(--color-background-border);--sd-color-tabs-overline:var(--color-background-border);--sd-color-tabs-underline:var(--color-background-border)}.sd-tab-content{box-shadow:0 -2px var(--sd-color-tabs-overline),0 1px var(--sd-color-tabs-underline)}.sd-card{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)}.sd-shadow-sm{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-md{box-shadow:0 .3rem .75rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-lg{box-shadow:0 .6rem 1.5rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-card-hover:hover{transform:none}.sd-cards-carousel{gap:.25rem;padding:.25rem}body{--tabs--label-text:var(--color-foreground-muted);--tabs--label-text--hover:var(--color-foreground-muted);--tabs--label-text--active:var(--color-brand-content);--tabs--label-text--active--hover:var(--color-brand-content);--tabs--label-background:transparent;--tabs--label-background--hover:transparent;--tabs--label-background--active:transparent;--tabs--label-background--active--hover:transparent;--tabs--padding-x:0.25em;--tabs--margin-x:1em;--tabs--border:var(--color-background-border);--tabs--label-border:transparent;--tabs--label-border--hover:var(--color-foreground-muted);--tabs--label-border--active:var(--color-brand-content);--tabs--label-border--active--hover:var(--color-brand-content)}[role=main] .container{max-width:none;padding-left:0;padding-right:0}.shadow.docutils{border:none;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)!important}.sphinx-bs .card{background-color:var(--color-background-secondary);color:var(--color-foreground)} +/*# sourceMappingURL=furo-extensions.css.map*/ \ No newline at end of file diff --git a/versions/0.2.2/_static/styles/furo-extensions.css.map b/versions/0.2.2/_static/styles/furo-extensions.css.map new file mode 100644 index 000000000..9ba5637f9 --- /dev/null +++ b/versions/0.2.2/_static/styles/furo-extensions.css.map @@ -0,0 +1 @@ +{"version":3,"file":"styles/furo-extensions.css","mappings":"AAGA,2BACE,oFACA,4CAKE,6CAHA,YACA,eAEA,CACA,kDACE,yCAEF,8CACE,sCAEJ,8CACE,kDAEJ,2BAGE,uBACA,cAHA,gBACA,UAEA,CAGA,yCACE,mBAEF,gDAEE,gDADA,YACA,CACA,sDACE,gDACF,yDACE,sCAEJ,+CACE,UACA,qDACE,UAGF,mDACE,eAEJ,yEAEE,4DAEA,mHASE,mBAPA,kBAEA,YADA,oBAGA,aADA,gBAIA,CAEA,qIAEE,WADA,UACA,CAEJ,uGACE,aAEF,iUAGE,cAEF,mHACE,aC1EJ,gCACE,mCAEF,0BAKE,mBAUA,8CACA,YAFA,mCAKA,eAZA,cALA,UASA,YADA,YAYA,iCAdA,YAcA,CAEA,gCAEE,8CADA,gCACA,CAEF,gCAGE,6BADA,mCADA,YAEA,CAEF,kCAEE,cADA,oBACA,CACA,wCACE,cAEJ,8BACE,UC5CN,KAEE,6CAA8C,CAC9C,uDAAwD,CACxD,uDAAwD,CAGxD,iCAAsC,CAGtC,+CAAgD,CAChD,uDAAwD,CACxD,uDAAwD,CACxD,oDAAqD,CACrD,6DAA8D,CAC9D,6DAA8D,CAG9D,uDAAwD,CACxD,yDAA0D,CAC1D,4DAA6D,CAC7D,2DAA4D,CAC5D,8DAA+D,CAC/D,iEAAkE,CAClE,uDAAwD,CACxD,wDAAyD,CAG3D,gBACE,qFAGF,SACE,6EAEF,cACE,uFAEF,cACE,uFAEF,cACE,uFAGF,qBACE,eAEF,mBACE,WACA,eChDF,KACE,gDAAiD,CACjD,uDAAwD,CACxD,qDAAsD,CACtD,4DAA6D,CAC7D,oCAAqC,CACrC,2CAA4C,CAC5C,4CAA6C,CAC7C,mDAAoD,CACpD,wBAAyB,CACzB,oBAAqB,CACrB,6CAA8C,CAC9C,gCAAiC,CACjC,yDAA0D,CAC1D,uDAAwD,CACxD,8DAA+D,CCbjE,uBACE,eACA,eACA,gBAGF,iBACE,YACA,+EAGF,iBACE,mDACA","sources":["webpack:///./src/furo/assets/styles/extensions/_readthedocs.sass","webpack:///./src/furo/assets/styles/extensions/_copybutton.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-design.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-inline-tabs.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-panels.sass"],"sourcesContent":["// This file contains the styles used for tweaking how ReadTheDoc's embedded\n// contents would show up inside the theme.\n\n#furo-sidebar-ad-placement\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n .ethical-sidebar\n // Remove the border and box-shadow.\n border: none\n box-shadow: none\n // Manage the background colors.\n background: var(--color-background-secondary)\n &:hover\n background: var(--color-background-hover)\n // Ensure the text is legible.\n a\n color: var(--color-foreground-primary)\n\n .ethical-callout a\n color: var(--color-foreground-secondary) !important\n\n#furo-readthedocs-versions\n position: static\n width: 100%\n background: transparent\n display: block\n\n // Make the background color fit with the theme's aesthetic.\n .rst-versions\n background: rgb(26, 28, 30)\n\n .rst-current-version\n cursor: unset\n background: var(--color-sidebar-item-background)\n &:hover\n background: var(--color-sidebar-item-background)\n .fa-book\n color: var(--color-foreground-primary)\n\n > .rst-other-versions\n padding: 0\n small\n opacity: 1\n\n .injected\n .rst-versions\n position: unset\n\n &:hover,\n &:focus-within\n box-shadow: 0 0 0 1px var(--color-sidebar-background-border)\n\n .rst-current-version\n // Undo the tweaks done in RTD's CSS\n font-size: inherit\n line-height: inherit\n height: auto\n text-align: right\n padding: 12px\n\n // Match the rest of the body\n background: #1a1c1e\n\n .fa-book\n float: left\n color: white\n\n .fa-caret-down\n display: none\n\n .rst-current-version,\n .rst-other-versions,\n .injected\n display: block\n\n > .rst-current-version\n display: none\n",".highlight\n &:hover button.copybtn\n color: var(--color-code-foreground)\n\n button.copybtn\n // Make it visible\n opacity: 1\n\n // Align things correctly\n align-items: center\n\n height: 1.25em\n width: 1.25em\n\n top: 0.625rem // $code-spacing-vertical\n right: 0.5rem\n\n // Make it look better\n color: var(--color-background-item)\n background-color: var(--color-code-background)\n border: none\n\n // Change to cursor to make it obvious that you can click on it\n cursor: pointer\n\n // Transition smoothly, for aesthetics\n transition: color 300ms, opacity 300ms\n\n &:hover\n color: var(--color-brand-content)\n background-color: var(--color-code-background)\n\n &::after\n display: none\n color: var(--color-code-foreground)\n background-color: transparent\n\n &.success\n transition: color 0ms\n color: #22863a\n &::after\n display: block\n\n svg\n padding: 0\n","body\n // Colors\n --sd-color-primary: var(--color-brand-primary)\n --sd-color-primary-highlight: var(--color-brand-content)\n --sd-color-primary-text: var(--color-background-primary)\n\n // Shadows\n --sd-color-shadow: rgba(0, 0, 0, 0.05)\n\n // Cards\n --sd-color-card-border: var(--color-card-border)\n --sd-color-card-border-hover: var(--color-brand-content)\n --sd-color-card-background: var(--color-card-background)\n --sd-color-card-text: var(--color-foreground-primary)\n --sd-color-card-header: var(--color-card-marginals-background)\n --sd-color-card-footer: var(--color-card-marginals-background)\n\n // Tabs\n --sd-color-tabs-label-active: var(--color-brand-content)\n --sd-color-tabs-label-hover: var(--color-foreground-muted)\n --sd-color-tabs-label-inactive: var(--color-foreground-muted)\n --sd-color-tabs-underline-active: var(--color-brand-content)\n --sd-color-tabs-underline-hover: var(--color-foreground-border)\n --sd-color-tabs-underline-inactive: var(--color-background-border)\n --sd-color-tabs-overline: var(--color-background-border)\n --sd-color-tabs-underline: var(--color-background-border)\n\n// Tabs\n.sd-tab-content\n box-shadow: 0 -2px var(--sd-color-tabs-overline), 0 1px var(--sd-color-tabs-underline)\n\n// Shadows\n.sd-card // Have a shadow by default\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n.sd-shadow-sm\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-md\n box-shadow: 0 0.3rem 0.75rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-lg\n box-shadow: 0 0.6rem 1.5rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Cards\n.sd-card-hover:hover // Don't change scale on hover\n transform: none\n\n.sd-cards-carousel // Have a bit of gap in the carousel by default\n gap: 0.25rem\n padding: 0.25rem\n","// This file contains styles to tweak sphinx-inline-tabs to work well with Furo.\n\nbody\n --tabs--label-text: var(--color-foreground-muted)\n --tabs--label-text--hover: var(--color-foreground-muted)\n --tabs--label-text--active: var(--color-brand-content)\n --tabs--label-text--active--hover: var(--color-brand-content)\n --tabs--label-background: transparent\n --tabs--label-background--hover: transparent\n --tabs--label-background--active: transparent\n --tabs--label-background--active--hover: transparent\n --tabs--padding-x: 0.25em\n --tabs--margin-x: 1em\n --tabs--border: var(--color-background-border)\n --tabs--label-border: transparent\n --tabs--label-border--hover: var(--color-foreground-muted)\n --tabs--label-border--active: var(--color-brand-content)\n --tabs--label-border--active--hover: var(--color-brand-content)\n","// This file contains styles to tweak sphinx-panels to work well with Furo.\n\n// sphinx-panels includes Bootstrap 4, which uses .container which can conflict\n// with docutils' `.. container::` directive.\n[role=\"main\"] .container\n max-width: initial\n padding-left: initial\n padding-right: initial\n\n// Make the panels look nicer!\n.shadow.docutils\n border: none\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Make panel colors respond to dark mode\n.sphinx-bs .card\n background-color: var(--color-background-secondary)\n color: var(--color-foreground)\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/versions/0.2.2/_static/styles/furo.css b/versions/0.2.2/_static/styles/furo.css new file mode 100644 index 000000000..3d29a218f --- /dev/null +++ b/versions/0.2.2/_static/styles/furo.css @@ -0,0 +1,2 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{-webkit-text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}@media print{.content-icon-container,.headerlink,.mobile-header,.related-pages{display:none!important}.highlight{border:.1pt solid var(--color-foreground-border)}a,blockquote,dl,ol,pre,table,ul{page-break-inside:avoid}caption,figure,h1,h2,h3,h4,h5,h6,img{page-break-after:avoid;page-break-inside:avoid}dl,ol,ul{page-break-before:avoid}}.visually-hidden{clip:rect(0,0,0,0)!important;border:0!important;height:1px!important;margin:-1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:1px!important}:-moz-focusring{outline:auto}body{--font-stack:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;--font-stack--monospace:"SFMono-Regular",Menlo,Consolas,Monaco,Liberation Mono,Lucida Console,monospace;--font-size--normal:100%;--font-size--small:87.5%;--font-size--small--2:81.25%;--font-size--small--3:75%;--font-size--small--4:62.5%;--sidebar-caption-font-size:var(--font-size--small--2);--sidebar-item-font-size:var(--font-size--small);--sidebar-search-input-font-size:var(--font-size--small);--toc-font-size:var(--font-size--small--3);--toc-font-size--mobile:var(--font-size--normal);--toc-title-font-size:var(--font-size--small--4);--admonition-font-size:0.8125rem;--admonition-title-font-size:0.8125rem;--code-font-size:var(--font-size--small--2);--api-font-size:var(--font-size--small);--header-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*4);--header-padding:0.5rem;--sidebar-tree-space-above:1.5rem;--sidebar-caption-space-above:1rem;--sidebar-item-line-height:1rem;--sidebar-item-spacing-vertical:0.5rem;--sidebar-item-spacing-horizontal:1rem;--sidebar-item-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*2);--sidebar-expander-width:var(--sidebar-item-height);--sidebar-search-space-above:0.5rem;--sidebar-search-input-spacing-vertical:0.5rem;--sidebar-search-input-spacing-horizontal:0.5rem;--sidebar-search-input-height:1rem;--sidebar-search-icon-size:var(--sidebar-search-input-height);--toc-title-padding:0.25rem 0;--toc-spacing-vertical:1.5rem;--toc-spacing-horizontal:1.5rem;--toc-item-spacing-vertical:0.4rem;--toc-item-spacing-horizontal:1rem;--icon-search:url('data:image/svg+xml;charset=utf-8,');--icon-pencil:url('data:image/svg+xml;charset=utf-8,');--icon-abstract:url('data:image/svg+xml;charset=utf-8,');--icon-info:url('data:image/svg+xml;charset=utf-8,');--icon-flame:url('data:image/svg+xml;charset=utf-8,');--icon-question:url('data:image/svg+xml;charset=utf-8,');--icon-warning:url('data:image/svg+xml;charset=utf-8,');--icon-failure:url('data:image/svg+xml;charset=utf-8,');--icon-spark:url('data:image/svg+xml;charset=utf-8,');--color-admonition-title--caution:#ff9100;--color-admonition-title-background--caution:rgba(255,145,0,.2);--color-admonition-title--warning:#ff9100;--color-admonition-title-background--warning:rgba(255,145,0,.2);--color-admonition-title--danger:#ff5252;--color-admonition-title-background--danger:rgba(255,82,82,.2);--color-admonition-title--attention:#ff5252;--color-admonition-title-background--attention:rgba(255,82,82,.2);--color-admonition-title--error:#ff5252;--color-admonition-title-background--error:rgba(255,82,82,.2);--color-admonition-title--hint:#00c852;--color-admonition-title-background--hint:rgba(0,200,82,.2);--color-admonition-title--tip:#00c852;--color-admonition-title-background--tip:rgba(0,200,82,.2);--color-admonition-title--important:#00bfa5;--color-admonition-title-background--important:rgba(0,191,165,.2);--color-admonition-title--note:#00b0ff;--color-admonition-title-background--note:rgba(0,176,255,.2);--color-admonition-title--seealso:#448aff;--color-admonition-title-background--seealso:rgba(68,138,255,.2);--color-admonition-title--admonition-todo:grey;--color-admonition-title-background--admonition-todo:hsla(0,0%,50%,.2);--color-admonition-title:#651fff;--color-admonition-title-background:rgba(101,31,255,.2);--icon-admonition-default:var(--icon-abstract);--color-topic-title:#14b8a6;--color-topic-title-background:rgba(20,184,166,.2);--icon-topic-default:var(--icon-pencil);--color-problematic:#b30000;--color-foreground-primary:#000;--color-foreground-secondary:#5a5c63;--color-foreground-muted:#646776;--color-foreground-border:#878787;--color-background-primary:#fff;--color-background-secondary:#f8f9fb;--color-background-hover:#efeff4;--color-background-hover--transparent:#efeff400;--color-background-border:#eeebee;--color-background-item:#ccc;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2962ff;--color-brand-content:#2a5adf;--color-api-background:var(--color-background-hover--transparent);--color-api-background-hover:var(--color-background-hover);--color-api-overall:var(--color-foreground-secondary);--color-api-name:var(--color-problematic);--color-api-pre-name:var(--color-problematic);--color-api-paren:var(--color-foreground-secondary);--color-api-keyword:var(--color-foreground-primary);--color-highlight-on-target:#ffc;--color-inline-code-background:var(--color-background-secondary);--color-highlighted-background:#def;--color-highlighted-text:var(--color-foreground-primary);--color-guilabel-background:#ddeeff80;--color-guilabel-border:#bedaf580;--color-guilabel-text:var(--color-foreground-primary);--color-admonition-background:transparent;--color-table-header-background:var(--color-background-secondary);--color-table-border:var(--color-background-border);--color-card-border:var(--color-background-secondary);--color-card-background:transparent;--color-card-marginals-background:var(--color-background-secondary);--color-header-background:var(--color-background-primary);--color-header-border:var(--color-background-border);--color-header-text:var(--color-foreground-primary);--color-sidebar-background:var(--color-background-secondary);--color-sidebar-background-border:var(--color-background-border);--color-sidebar-brand-text:var(--color-foreground-primary);--color-sidebar-caption-text:var(--color-foreground-muted);--color-sidebar-link-text:var(--color-foreground-secondary);--color-sidebar-link-text--top-level:var(--color-brand-primary);--color-sidebar-item-background:var(--color-sidebar-background);--color-sidebar-item-background--current:var( --color-sidebar-item-background );--color-sidebar-item-background--hover:linear-gradient(90deg,var(--color-background-hover--transparent) 0%,var(--color-background-hover) var(--sidebar-item-spacing-horizontal),var(--color-background-hover) 100%);--color-sidebar-item-expander-background:transparent;--color-sidebar-item-expander-background--hover:var( --color-background-hover );--color-sidebar-search-text:var(--color-foreground-primary);--color-sidebar-search-background:var(--color-background-secondary);--color-sidebar-search-background--focus:var(--color-background-primary);--color-sidebar-search-border:var(--color-background-border);--color-sidebar-search-icon:var(--color-foreground-muted);--color-toc-background:var(--color-background-primary);--color-toc-title-text:var(--color-foreground-muted);--color-toc-item-text:var(--color-foreground-secondary);--color-toc-item-text--hover:var(--color-foreground-primary);--color-toc-item-text--active:var(--color-brand-primary);--color-content-foreground:var(--color-foreground-primary);--color-content-background:transparent;--color-link:var(--color-brand-content);--color-link--hover:var(--color-brand-content);--color-link-underline:var(--color-background-border);--color-link-underline--hover:var(--color-foreground-border)}.only-light{display:block!important}html body .only-dark{display:none!important}@media not print{body[data-theme=dark]{--color-problematic:#ee5151;--color-foreground-primary:#ffffffcc;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2b8cee;--color-brand-content:#368ce2;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body[data-theme=dark] .only-light{display:none!important}body[data-theme=dark] .only-dark{display:block!important}@media(prefers-color-scheme:dark){body:not([data-theme=light]){--color-problematic:#ee5151;--color-foreground-primary:#ffffffcc;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2b8cee;--color-brand-content:#368ce2;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body:not([data-theme=light]) .only-light{display:none!important}body:not([data-theme=light]) .only-dark{display:block!important}}}body[data-theme=auto] .theme-toggle svg.theme-icon-when-auto,body[data-theme=dark] .theme-toggle svg.theme-icon-when-dark,body[data-theme=light] .theme-toggle svg.theme-icon-when-light{display:block}body{font-family:var(--font-stack)}code,kbd,pre,samp{font-family:var(--font-stack--monospace)}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}article{line-height:1.5}h1,h2,h3,h4,h5,h6{border-radius:.5rem;font-weight:700;line-height:1.25;margin:.5rem -.5rem;padding-left:.5rem;padding-right:.5rem}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:0}h1{font-size:2.5em;margin-bottom:1rem}h1,h2{margin-top:1.75rem}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.25em}h5{font-size:1.125em}h6{font-size:1em}small{font-size:80%;opacity:75%}p{margin-bottom:.75rem;margin-top:.5rem}hr.docutils{background-color:var(--color-background-border);border:0;height:1px;margin:2rem 0;padding:0}.centered{text-align:center}a{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}a:hover{color:var(--color-link--hover);text-decoration-color:var(--color-link-underline--hover)}a.muted-link{color:inherit}a.muted-link:hover{color:var(--color-link);text-decoration-color:var(--color-link-underline--hover)}html{overflow-x:hidden;overflow-y:scroll;scroll-behavior:smooth}.sidebar-scroll,.toc-scroll,article[role=main] *{scrollbar-color:var(--color-foreground-border) transparent;scrollbar-width:thin}.sidebar-scroll::-webkit-scrollbar,.toc-scroll::-webkit-scrollbar,article[role=main] ::-webkit-scrollbar{height:.25rem;width:.25rem}.sidebar-scroll::-webkit-scrollbar-thumb,.toc-scroll::-webkit-scrollbar-thumb,article[role=main] ::-webkit-scrollbar-thumb{background-color:var(--color-foreground-border);border-radius:.125rem}body,html{background:var(--color-background-primary);color:var(--color-foreground-primary);height:100%}article{background:var(--color-content-background);color:var(--color-content-foreground);overflow-wrap:break-word}.page{display:flex;min-height:100%}.mobile-header{background-color:var(--color-header-background);border-bottom:1px solid var(--color-header-border);color:var(--color-header-text);display:none;height:var(--header-height);width:100%;z-index:10}.mobile-header.scrolled{border-bottom:none;box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2)}.mobile-header .header-center a{color:var(--color-header-text);text-decoration:none}.main{display:flex;flex:1}.sidebar-drawer{background:var(--color-sidebar-background);border-right:1px solid var(--color-sidebar-background-border);box-sizing:border-box;display:flex;justify-content:flex-end;min-width:15em;width:calc(50% - 26em)}.sidebar-container,.toc-drawer{box-sizing:border-box;width:15em}.toc-drawer{background:var(--color-toc-background);padding-right:1rem}.sidebar-sticky,.toc-sticky{display:flex;flex-direction:column;height:min(100%,100vh);height:100vh;position:sticky;top:0}.sidebar-scroll,.toc-scroll{flex-grow:1;flex-shrink:1;overflow:auto;scroll-behavior:smooth}.content{display:flex;flex-direction:column;justify-content:space-between;padding:0 3em;width:46em}.icon{display:inline-block;height:1rem;width:1rem}.icon svg{height:100%;width:100%}.announcement{align-items:center;background-color:var(--color-announcement-background);color:var(--color-announcement-text);display:flex;height:var(--header-height);overflow-x:auto}.announcement+.page{min-height:calc(100% - var(--header-height))}.announcement-content{box-sizing:border-box;min-width:100%;padding:.5rem;text-align:center;white-space:nowrap}.announcement-content a{color:var(--color-announcement-text);text-decoration-color:var(--color-announcement-text)}.announcement-content a:hover{color:var(--color-announcement-text);text-decoration-color:var(--color-link--hover)}.no-js .theme-toggle-container{display:none}.theme-toggle-container{vertical-align:middle}.theme-toggle{background:transparent;border:none;cursor:pointer;padding:0}.theme-toggle svg{color:var(--color-foreground-primary);display:none;height:1rem;vertical-align:middle;width:1rem}.theme-toggle-header{float:left;padding:1rem .5rem}.nav-overlay-icon,.toc-overlay-icon{cursor:pointer;display:none}.nav-overlay-icon .icon,.toc-overlay-icon .icon{color:var(--color-foreground-secondary);height:1rem;width:1rem}.nav-overlay-icon,.toc-header-icon{align-items:center;justify-content:center}.toc-content-icon{height:1.5rem;width:1.5rem}.content-icon-container{display:flex;float:right;gap:.5rem;margin-bottom:1rem;margin-left:1rem;margin-top:1.5rem}.content-icon-container .edit-this-page svg{color:inherit;height:1rem;width:1rem}.sidebar-toggle{display:none;position:absolute}.sidebar-toggle[name=__toc]{left:20px}.sidebar-toggle:checked{left:40px}.overlay{background-color:rgba(0,0,0,.54);height:0;opacity:0;position:fixed;top:0;transition:width 0ms,height 0ms,opacity .25s ease-out;width:0}.sidebar-overlay{z-index:20}.toc-overlay{z-index:40}.sidebar-drawer{transition:left .25s ease-in-out;z-index:30}.toc-drawer{transition:right .25s ease-in-out;z-index:50}#__navigation:checked~.sidebar-overlay{height:100%;opacity:1;width:100%}#__navigation:checked~.page .sidebar-drawer{left:0;top:0}#__toc:checked~.toc-overlay{height:100%;opacity:1;width:100%}#__toc:checked~.page .toc-drawer{right:0;top:0}.back-to-top{background:var(--color-background-primary);border-radius:1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 1px 0 hsla(220,9%,46%,.502);display:none;font-size:.8125rem;left:0;margin-left:50%;padding:.5rem .75rem .5rem .5rem;position:fixed;text-decoration:none;top:1rem;transform:translateX(-50%);z-index:10}.back-to-top svg{fill:currentColor;display:inline-block;height:1rem;width:1rem}.back-to-top span{margin-left:.25rem}.show-back-to-top .back-to-top{align-items:center;display:flex}@media(min-width:97em){html{font-size:110%}}@media(max-width:82em){.toc-content-icon{display:flex}.toc-drawer{border-left:1px solid var(--color-background-muted);height:100vh;position:fixed;right:-15em;top:0}.toc-tree{border-left:none;font-size:var(--toc-font-size--mobile)}.sidebar-drawer{width:calc(50% - 18.5em)}}@media(max-width:67em){.nav-overlay-icon{display:flex}.sidebar-drawer{height:100vh;left:-15em;position:fixed;top:0;width:15em}.toc-header-icon{display:flex}.theme-toggle-content,.toc-content-icon{display:none}.theme-toggle-header{display:block}.mobile-header{align-items:center;display:flex;justify-content:space-between;position:sticky;top:0}.mobile-header .header-left,.mobile-header .header-right{display:flex;height:var(--header-height);padding:0 var(--header-padding)}.mobile-header .header-left label,.mobile-header .header-right label{height:100%;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.nav-overlay-icon .icon,.theme-toggle svg{height:1.25rem;width:1.25rem}:target{scroll-margin-top:var(--header-height)}.back-to-top{top:calc(var(--header-height) + .5rem)}.page{flex-direction:column;justify-content:center}.content{margin-left:auto;margin-right:auto}}@media(max-width:52em){.content{overflow-x:auto;width:100%}}@media(max-width:46em){.content{padding:0 1em}article aside.sidebar{float:none;margin:1rem 0;width:100%}}.admonition,.topic{background:var(--color-admonition-background);border-radius:.2rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1);font-size:var(--admonition-font-size);margin:1rem auto;overflow:hidden;padding:0 .5rem .5rem;page-break-inside:avoid}.admonition>:nth-child(2),.topic>:nth-child(2){margin-top:0}.admonition>:last-child,.topic>:last-child{margin-bottom:0}.admonition p.admonition-title,p.topic-title{font-size:var(--admonition-title-font-size);font-weight:500;line-height:1.3;margin:0 -.5rem .5rem;padding:.4rem .5rem .4rem 2rem;position:relative}.admonition p.admonition-title:before,p.topic-title:before{content:"";height:1rem;left:.5rem;position:absolute;width:1rem}p.admonition-title{background-color:var(--color-admonition-title-background)}p.admonition-title:before{background-color:var(--color-admonition-title);-webkit-mask-image:var(--icon-admonition-default);mask-image:var(--icon-admonition-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}p.topic-title{background-color:var(--color-topic-title-background)}p.topic-title:before{background-color:var(--color-topic-title);-webkit-mask-image:var(--icon-topic-default);mask-image:var(--icon-topic-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.admonition{border-left:.2rem solid var(--color-admonition-title)}.admonition.caution{border-left-color:var(--color-admonition-title--caution)}.admonition.caution>.admonition-title{background-color:var(--color-admonition-title-background--caution)}.admonition.caution>.admonition-title:before{background-color:var(--color-admonition-title--caution);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.warning{border-left-color:var(--color-admonition-title--warning)}.admonition.warning>.admonition-title{background-color:var(--color-admonition-title-background--warning)}.admonition.warning>.admonition-title:before{background-color:var(--color-admonition-title--warning);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.danger{border-left-color:var(--color-admonition-title--danger)}.admonition.danger>.admonition-title{background-color:var(--color-admonition-title-background--danger)}.admonition.danger>.admonition-title:before{background-color:var(--color-admonition-title--danger);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.attention{border-left-color:var(--color-admonition-title--attention)}.admonition.attention>.admonition-title{background-color:var(--color-admonition-title-background--attention)}.admonition.attention>.admonition-title:before{background-color:var(--color-admonition-title--attention);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.error{border-left-color:var(--color-admonition-title--error)}.admonition.error>.admonition-title{background-color:var(--color-admonition-title-background--error)}.admonition.error>.admonition-title:before{background-color:var(--color-admonition-title--error);-webkit-mask-image:var(--icon-failure);mask-image:var(--icon-failure)}.admonition.hint{border-left-color:var(--color-admonition-title--hint)}.admonition.hint>.admonition-title{background-color:var(--color-admonition-title-background--hint)}.admonition.hint>.admonition-title:before{background-color:var(--color-admonition-title--hint);-webkit-mask-image:var(--icon-question);mask-image:var(--icon-question)}.admonition.tip{border-left-color:var(--color-admonition-title--tip)}.admonition.tip>.admonition-title{background-color:var(--color-admonition-title-background--tip)}.admonition.tip>.admonition-title:before{background-color:var(--color-admonition-title--tip);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.important{border-left-color:var(--color-admonition-title--important)}.admonition.important>.admonition-title{background-color:var(--color-admonition-title-background--important)}.admonition.important>.admonition-title:before{background-color:var(--color-admonition-title--important);-webkit-mask-image:var(--icon-flame);mask-image:var(--icon-flame)}.admonition.note{border-left-color:var(--color-admonition-title--note)}.admonition.note>.admonition-title{background-color:var(--color-admonition-title-background--note)}.admonition.note>.admonition-title:before{background-color:var(--color-admonition-title--note);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition.seealso{border-left-color:var(--color-admonition-title--seealso)}.admonition.seealso>.admonition-title{background-color:var(--color-admonition-title-background--seealso)}.admonition.seealso>.admonition-title:before{background-color:var(--color-admonition-title--seealso);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.admonition-todo{border-left-color:var(--color-admonition-title--admonition-todo)}.admonition.admonition-todo>.admonition-title{background-color:var(--color-admonition-title-background--admonition-todo)}.admonition.admonition-todo>.admonition-title:before{background-color:var(--color-admonition-title--admonition-todo);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition-todo>.admonition-title{text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd{margin-left:2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:first-child{margin-top:.125rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list,dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:last-child{margin-bottom:.75rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list>dt{font-size:var(--font-size--small);text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd:empty{margin-bottom:.5rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul{margin-left:-1.2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p:nth-child(2){margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p+p:last-child:empty{margin-bottom:0;margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt{color:var(--color-api-overall)}.sig:not(.sig-inline){background:var(--color-api-background);border-radius:.25rem;font-family:var(--font-stack--monospace);font-size:var(--api-font-size);font-weight:700;margin-left:-.25rem;margin-right:-.25rem;padding:.25rem .5rem .25rem 3em;text-indent:-2.5em;transition:background .1s ease-out}.sig:not(.sig-inline):hover{background:var(--color-api-background-hover)}.sig:not(.sig-inline) a.reference .viewcode-link{font-weight:400;width:3.5rem}em.property{font-style:normal}em.property:first-child{color:var(--color-api-keyword)}.sig-name{color:var(--color-api-name)}.sig-prename{color:var(--color-api-pre-name);font-weight:400}.sig-paren{color:var(--color-api-paren)}.sig-param{font-style:normal}.versionmodified{font-style:italic}div.deprecated p,div.versionadded p,div.versionchanged p{margin-bottom:.125rem;margin-top:.125rem}.viewcode-back,.viewcode-link{float:right;text-align:right}.line-block{margin-bottom:.75rem;margin-top:.5rem}.line-block .line-block{margin-bottom:0;margin-top:0;padding-left:1rem}.code-block-caption,article p.caption,table>caption{font-size:var(--font-size--small);text-align:center}.toctree-wrapper.compound .caption,.toctree-wrapper.compound :not(.caption)>.caption-text{font-size:var(--font-size--small);margin-bottom:0;text-align:initial;text-transform:uppercase}.toctree-wrapper.compound>ul{margin-bottom:0;margin-top:0}.sig-inline,code.literal{background:var(--color-inline-code-background);border-radius:.2em;font-size:var(--font-size--small--2);padding:.1em .2em}pre.literal-block .sig-inline,pre.literal-block code.literal{font-size:inherit;padding:0}p .sig-inline,p code.literal{border:1px solid var(--color-background-border)}.sig-inline{font-family:var(--font-stack--monospace)}div[class*=" highlight-"],div[class^=highlight-]{display:flex;margin:1em 0}div[class*=" highlight-"] .table-wrapper,div[class^=highlight-] .table-wrapper,pre{margin:0;padding:0}pre{overflow:auto}article[role=main] .highlight pre{line-height:1.5}.highlight pre,pre.literal-block{font-size:var(--code-font-size);padding:.625rem .875rem}pre.literal-block{background-color:var(--color-code-background);border-radius:.2rem;color:var(--color-code-foreground);margin-bottom:1rem;margin-top:1rem}.highlight{border-radius:.2rem;width:100%}.highlight .gp,.highlight span.linenos{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.highlight .hll{display:block;margin-left:-.875rem;margin-right:-.875rem;padding-left:.875rem;padding-right:.875rem}.code-block-caption{background-color:var(--color-code-background);border-bottom:1px solid;border-radius:.25rem;border-bottom-left-radius:0;border-bottom-right-radius:0;border-color:var(--color-background-border);color:var(--color-code-foreground);display:flex;font-weight:300;padding:.625rem .875rem}.code-block-caption+div[class]{margin-top:0}.code-block-caption+div[class] pre{border-top-left-radius:0;border-top-right-radius:0}.highlighttable{display:block;width:100%}.highlighttable tbody{display:block}.highlighttable tr{display:flex}.highlighttable td.linenos{background-color:var(--color-code-background);border-bottom-left-radius:.2rem;border-top-left-radius:.2rem;color:var(--color-code-foreground);padding:.625rem 0 .625rem .875rem}.highlighttable .linenodiv{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;font-size:var(--code-font-size);padding-right:.875rem}.highlighttable td.code{display:block;flex:1;overflow:hidden;padding:0}.highlighttable td.code .highlight{border-bottom-left-radius:0;border-top-left-radius:0}.highlight span.linenos{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;display:inline-block;margin-right:.875rem;padding-left:0;padding-right:.875rem}.footnote-reference{font-size:var(--font-size--small--4);vertical-align:super}dl.footnote.brackets{color:var(--color-foreground-secondary);display:grid;font-size:var(--font-size--small);grid-template-columns:max-content auto}dl.footnote.brackets dt{margin:0}dl.footnote.brackets dt>.fn-backref{margin-left:.25rem}dl.footnote.brackets dt:after{content:":"}dl.footnote.brackets dt .brackets:before{content:"["}dl.footnote.brackets dt .brackets:after{content:"]"}dl.footnote.brackets dd{margin:0;padding:0 1rem}aside.footnote{color:var(--color-foreground-secondary);font-size:var(--font-size--small)}aside.footnote>span,div.citation>span{float:left;font-weight:500;padding-right:.25rem}aside.footnote>p,div.citation>p{margin-left:2rem}img{box-sizing:border-box;height:auto;max-width:100%}article .figure,article figure{border-radius:.2rem;margin:0}article .figure :last-child,article figure :last-child{margin-bottom:0}article .align-left{clear:left;float:left;margin:0 1rem 1rem}article .align-right{clear:right;float:right;margin:0 1rem 1rem}article .align-center,article .align-default{display:block;margin-left:auto;margin-right:auto;text-align:center}article table.align-default{display:table;text-align:initial}.domainindex-jumpbox,.genindex-jumpbox{border-bottom:1px solid var(--color-background-border);border-top:1px solid var(--color-background-border);padding:.25rem}.domainindex-section h2,.genindex-section h2{margin-bottom:.5rem;margin-top:.75rem}.domainindex-section ul,.genindex-section ul{margin-bottom:0;margin-top:0}ol,ul{margin-bottom:1rem;margin-top:1rem;padding-left:1.2rem}ol li>p:first-child,ul li>p:first-child{margin-bottom:.25rem;margin-top:.25rem}ol li>p:last-child,ul li>p:last-child{margin-top:.25rem}ol li>ol,ol li>ul,ul li>ol,ul li>ul{margin-bottom:.5rem;margin-top:.5rem}ol.arabic{list-style:decimal}ol.loweralpha{list-style:lower-alpha}ol.upperalpha{list-style:upper-alpha}ol.lowerroman{list-style:lower-roman}ol.upperroman{list-style:upper-roman}.simple li>ol,.simple li>ul,.toctree-wrapper li>ol,.toctree-wrapper li>ul{margin-bottom:0;margin-top:0}.field-list dt,.option-list dt,dl.footnote dt,dl.glossary dt,dl.simple dt,dl:not([class]) dt{font-weight:500;margin-top:.25rem}.field-list dt+dt,.option-list dt+dt,dl.footnote dt+dt,dl.glossary dt+dt,dl.simple dt+dt,dl:not([class]) dt+dt{margin-top:0}.field-list dt .classifier:before,.option-list dt .classifier:before,dl.footnote dt .classifier:before,dl.glossary dt .classifier:before,dl.simple dt .classifier:before,dl:not([class]) dt .classifier:before{content:":";margin-left:.2rem;margin-right:.2rem}.field-list dd ul,.field-list dd>p:first-child,.option-list dd ul,.option-list dd>p:first-child,dl.footnote dd ul,dl.footnote dd>p:first-child,dl.glossary dd ul,dl.glossary dd>p:first-child,dl.simple dd ul,dl.simple dd>p:first-child,dl:not([class]) dd ul,dl:not([class]) dd>p:first-child{margin-top:.125rem}.field-list dd ul,.option-list dd ul,dl.footnote dd ul,dl.glossary dd ul,dl.simple dd ul,dl:not([class]) dd ul{margin-bottom:.125rem}.math-wrapper{overflow-x:auto;width:100%}div.math{position:relative;text-align:center}div.math .headerlink,div.math:focus .headerlink{display:none}div.math:hover .headerlink{display:inline-block}div.math span.eqno{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);z-index:1}abbr[title]{cursor:help}.problematic{color:var(--color-problematic)}kbd:not(.compound){background-color:var(--color-background-secondary);border:1px solid var(--color-foreground-border);border-radius:.2rem;box-shadow:0 .0625rem 0 rgba(0,0,0,.2),inset 0 0 0 .125rem var(--color-background-primary);color:var(--color-foreground-primary);display:inline-block;font-size:var(--font-size--small--3);margin:0 .2rem;padding:0 .2rem;vertical-align:text-bottom}blockquote{background:var(--color-background-secondary);border-left:4px solid var(--color-background-border);margin-left:0;margin-right:0;padding:.5rem 1rem}blockquote .attribution{font-weight:600;text-align:right}blockquote.highlights,blockquote.pull-quote{font-size:1.25em}blockquote.epigraph,blockquote.pull-quote{border-left-width:0;border-radius:.5rem}blockquote.highlights{background:transparent;border-left-width:0}p .reference img{vertical-align:middle}p.rubric{font-size:1.125em;font-weight:700;line-height:1.25}dd p.rubric{font-size:var(--font-size--small);font-weight:inherit;line-height:inherit;text-transform:uppercase}article .sidebar{background-color:var(--color-background-secondary);border:1px solid var(--color-background-border);border-radius:.2rem;clear:right;float:right;margin-left:1rem;margin-right:0;width:30%}article .sidebar>*{padding-left:1rem;padding-right:1rem}article .sidebar>ol,article .sidebar>ul{padding-left:2.2rem}article .sidebar .sidebar-title{border-bottom:1px solid var(--color-background-border);font-weight:500;margin:0;padding:.5rem 1rem}.table-wrapper{margin-bottom:.5rem;margin-top:1rem;overflow-x:auto;padding:.2rem .2rem .75rem;width:100%}table.docutils{border-collapse:collapse;border-radius:.2rem;border-spacing:0;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)}table.docutils th{background:var(--color-table-header-background)}table.docutils td,table.docutils th{border-bottom:1px solid var(--color-table-border);border-left:1px solid var(--color-table-border);border-right:1px solid var(--color-table-border);padding:0 .25rem}table.docutils td p,table.docutils th p{margin:.25rem}table.docutils td:first-child,table.docutils th:first-child{border-left:none}table.docutils td:last-child,table.docutils th:last-child{border-right:none}table.docutils td.text-left,table.docutils th.text-left{text-align:left}table.docutils td.text-right,table.docutils th.text-right{text-align:right}table.docutils td.text-center,table.docutils th.text-center{text-align:center}:target{scroll-margin-top:.5rem}@media(max-width:67em){:target{scroll-margin-top:calc(.5rem + var(--header-height))}section>span:target{scroll-margin-top:calc(.8rem + var(--header-height))}}.headerlink{font-weight:100;-webkit-user-select:none;-moz-user-select:none;user-select:none}.code-block-caption>.headerlink,dl dt>.headerlink,figcaption p>.headerlink,h1>.headerlink,h2>.headerlink,h3>.headerlink,h4>.headerlink,h5>.headerlink,h6>.headerlink,p.caption>.headerlink,table>caption>.headerlink{margin-left:.5rem;visibility:hidden}.code-block-caption:hover>.headerlink,dl dt:hover>.headerlink,figcaption p:hover>.headerlink,h1:hover>.headerlink,h2:hover>.headerlink,h3:hover>.headerlink,h4:hover>.headerlink,h5:hover>.headerlink,h6:hover>.headerlink,p.caption:hover>.headerlink,table>caption:hover>.headerlink{visibility:visible}.code-block-caption>.toc-backref,dl dt>.toc-backref,figcaption p>.toc-backref,h1>.toc-backref,h2>.toc-backref,h3>.toc-backref,h4>.toc-backref,h5>.toc-backref,h6>.toc-backref,p.caption>.toc-backref,table>caption>.toc-backref{color:inherit;text-decoration-line:none}figure:hover>figcaption>p>.headerlink,table:hover>caption>.headerlink{visibility:visible}:target>h1:first-of-type,:target>h2:first-of-type,:target>h3:first-of-type,:target>h4:first-of-type,:target>h5:first-of-type,:target>h6:first-of-type,span:target~h1:first-of-type,span:target~h2:first-of-type,span:target~h3:first-of-type,span:target~h4:first-of-type,span:target~h5:first-of-type,span:target~h6:first-of-type{background-color:var(--color-highlight-on-target)}:target>h1:first-of-type code.literal,:target>h2:first-of-type code.literal,:target>h3:first-of-type code.literal,:target>h4:first-of-type code.literal,:target>h5:first-of-type code.literal,:target>h6:first-of-type code.literal,span:target~h1:first-of-type code.literal,span:target~h2:first-of-type code.literal,span:target~h3:first-of-type code.literal,span:target~h4:first-of-type code.literal,span:target~h5:first-of-type code.literal,span:target~h6:first-of-type code.literal{background-color:transparent}.literal-block-wrapper:target .code-block-caption,.this-will-duplicate-information-and-it-is-still-useful-here li :target,figure:target,table:target>caption{background-color:var(--color-highlight-on-target)}dt:target{background-color:var(--color-highlight-on-target)!important}.footnote-reference:target,.footnote>dt:target+dd{background-color:var(--color-highlight-on-target)}.guilabel{background-color:var(--color-guilabel-background);border:1px solid var(--color-guilabel-border);border-radius:.5em;color:var(--color-guilabel-text);font-size:.9em;padding:0 .3em}footer{display:flex;flex-direction:column;font-size:var(--font-size--small);margin-top:2rem}.bottom-of-page{align-items:center;border-top:1px solid var(--color-background-border);color:var(--color-foreground-secondary);display:flex;justify-content:space-between;line-height:1.5;margin-top:1rem;padding-bottom:1rem;padding-top:1rem}@media(max-width:46em){.bottom-of-page{flex-direction:column-reverse;gap:.25rem;text-align:center}}.bottom-of-page .left-details{font-size:var(--font-size--small)}.bottom-of-page .right-details{display:flex;flex-direction:column;gap:.25rem;text-align:right}.bottom-of-page .icons{display:flex;font-size:1rem;gap:.25rem;justify-content:flex-end}.bottom-of-page .icons a{text-decoration:none}.bottom-of-page .icons img,.bottom-of-page .icons svg{font-size:1.125rem;height:1em;width:1em}.related-pages a{align-items:center;display:flex;text-decoration:none}.related-pages a:hover .page-info .title{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}.related-pages a svg.furo-related-icon,.related-pages a svg.furo-related-icon>use{color:var(--color-foreground-border);flex-shrink:0;height:.75rem;margin:0 .5rem;width:.75rem}.related-pages a.next-page{clear:right;float:right;max-width:50%;text-align:right}.related-pages a.prev-page{clear:left;float:left;max-width:50%}.related-pages a.prev-page svg{transform:rotate(180deg)}.page-info{display:flex;flex-direction:column;overflow-wrap:anywhere}.next-page .page-info{align-items:flex-end}.page-info .context{align-items:center;color:var(--color-foreground-muted);display:flex;font-size:var(--font-size--small);padding-bottom:.1rem;text-decoration:none}ul.search{list-style:none;padding-left:0}ul.search li{border-bottom:1px solid var(--color-background-border);padding:1rem 0}[role=main] .highlighted{background-color:var(--color-highlighted-background);color:var(--color-highlighted-text)}.sidebar-brand{display:flex;flex-direction:column;flex-shrink:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none}.sidebar-brand-text{color:var(--color-sidebar-brand-text);font-size:1.5rem;overflow-wrap:break-word}.sidebar-brand-text,.sidebar-logo-container{margin:var(--sidebar-item-spacing-vertical) 0}.sidebar-logo{display:block;margin:0 auto;max-width:100%}.sidebar-search-container{align-items:center;background:var(--color-sidebar-search-background);display:flex;margin-top:var(--sidebar-search-space-above);position:relative}.sidebar-search-container:focus-within,.sidebar-search-container:hover{background:var(--color-sidebar-search-background--focus)}.sidebar-search-container:before{background-color:var(--color-sidebar-search-icon);content:"";height:var(--sidebar-search-icon-size);left:var(--sidebar-item-spacing-horizontal);-webkit-mask-image:var(--icon-search);mask-image:var(--icon-search);position:absolute;width:var(--sidebar-search-icon-size)}.sidebar-search{background:transparent;border:none;border-bottom:1px solid var(--color-sidebar-search-border);border-top:1px solid var(--color-sidebar-search-border);box-sizing:border-box;color:var(--color-sidebar-search-foreground);padding:var(--sidebar-search-input-spacing-vertical) var(--sidebar-search-input-spacing-horizontal) var(--sidebar-search-input-spacing-vertical) calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size));width:100%;z-index:10}.sidebar-search:focus{outline:none}.sidebar-search::-moz-placeholder{font-size:var(--sidebar-search-input-font-size)}.sidebar-search::placeholder{font-size:var(--sidebar-search-input-font-size)}#searchbox .highlight-link{margin:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0;text-align:center}#searchbox .highlight-link a{color:var(--color-sidebar-search-icon);font-size:var(--font-size--small--2)}.sidebar-tree{font-size:var(--sidebar-item-font-size);margin-bottom:var(--sidebar-item-spacing-vertical);margin-top:var(--sidebar-tree-space-above)}.sidebar-tree ul{display:flex;flex-direction:column;list-style:none;margin-bottom:0;margin-top:0;padding:0}.sidebar-tree li{margin:0;position:relative}.sidebar-tree li>ul{margin-left:var(--sidebar-item-spacing-horizontal)}.sidebar-tree .icon,.sidebar-tree .reference{color:var(--color-sidebar-link-text)}.sidebar-tree .reference{box-sizing:border-box;display:inline-block;height:100%;line-height:var(--sidebar-item-line-height);overflow-wrap:anywhere;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none;width:100%}.sidebar-tree .reference:hover{background:var(--color-sidebar-item-background--hover)}.sidebar-tree .reference.external:after{color:var(--color-sidebar-link-text);content:url("data:image/svg+xml;charset=utf-8,%3Csvg width='12' height='12' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke-width='1.5' stroke='%23607D8B' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M0 0h24v24H0z' stroke='none'/%3E%3Cpath d='M11 7H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-5M10 14 20 4M15 4h5v5'/%3E%3C/svg%3E");margin:0 .25rem;vertical-align:middle}.sidebar-tree .current-page>.reference{font-weight:700}.sidebar-tree label{align-items:center;cursor:pointer;display:flex;height:var(--sidebar-item-height);justify-content:center;position:absolute;right:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:var(--sidebar-expander-width)}.sidebar-tree .caption,.sidebar-tree :not(.caption)>.caption-text{color:var(--color-sidebar-caption-text);font-size:var(--sidebar-caption-font-size);font-weight:700;margin:var(--sidebar-caption-space-above) 0 0 0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-transform:uppercase}.sidebar-tree li.has-children>.reference{padding-right:var(--sidebar-expander-width)}.sidebar-tree .toctree-l1>.reference,.sidebar-tree .toctree-l1>label .icon{color:var(--color-sidebar-link-text--top-level)}.sidebar-tree label{background:var(--color-sidebar-item-expander-background)}.sidebar-tree label:hover{background:var(--color-sidebar-item-expander-background--hover)}.sidebar-tree .current>.reference{background:var(--color-sidebar-item-background--current)}.sidebar-tree .current>.reference:hover{background:var(--color-sidebar-item-background--hover)}.toctree-checkbox{display:none;position:absolute}.toctree-checkbox~ul{display:none}.toctree-checkbox~label .icon svg{transform:rotate(90deg)}.toctree-checkbox:checked~ul{display:block}.toctree-checkbox:checked~label .icon svg{transform:rotate(-90deg)}.toc-title-container{padding:var(--toc-title-padding);padding-top:var(--toc-spacing-vertical)}.toc-title{color:var(--color-toc-title-text);font-size:var(--toc-title-font-size);padding-left:var(--toc-spacing-horizontal);text-transform:uppercase}.no-toc{display:none}.toc-tree-container{padding-bottom:var(--toc-spacing-vertical)}.toc-tree{border-left:1px solid var(--color-background-border);font-size:var(--toc-font-size);line-height:1.3;padding-left:calc(var(--toc-spacing-horizontal) - var(--toc-item-spacing-horizontal))}.toc-tree>ul>li:first-child{padding-top:0}.toc-tree>ul>li:first-child>ul{padding-left:0}.toc-tree>ul>li:first-child>a{display:none}.toc-tree ul{list-style-type:none;margin-bottom:0;margin-top:0;padding-left:var(--toc-item-spacing-horizontal)}.toc-tree li{padding-top:var(--toc-item-spacing-vertical)}.toc-tree li.scroll-current>.reference{color:var(--color-toc-item-text--active);font-weight:700}.toc-tree .reference{color:var(--color-toc-item-text);overflow-wrap:anywhere;text-decoration:none}.toc-scroll{max-height:100vh;overflow-y:scroll}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here){background:rgba(255,0,0,.25);color:var(--color-problematic)}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here):before{content:"ERROR: Adding a table of contents in Furo-based documentation is unnecessary, and does not work well with existing styling.Add a 'this-will-duplicate-information-and-it-is-still-useful-here' class, if you want an escape hatch."}.text-align\:left>p{text-align:left}.text-align\:center>p{text-align:center}.text-align\:right>p{text-align:right} +/*# sourceMappingURL=furo.css.map*/ \ No newline at end of file diff --git a/versions/0.2.2/_static/styles/furo.css.map b/versions/0.2.2/_static/styles/furo.css.map new file mode 100644 index 000000000..d1dfb109d --- /dev/null +++ b/versions/0.2.2/_static/styles/furo.css.map @@ -0,0 +1 @@ +{"version":3,"file":"styles/furo.css","mappings":"AAAA,2EAA2E,CAU3E,KAEE,6BAA8B,CAD9B,gBAEF,CASA,KACE,QACF,CAMA,KACE,aACF,CAOA,GACE,aAAc,CACd,cACF,CAUA,GACE,sBAAuB,CACvB,QAAS,CACT,gBACF,CAOA,IACE,+BAAiC,CACjC,aACF,CASA,EACE,4BACF,CAOA,YACE,kBAAmB,CACnB,yBAA0B,CAC1B,gCACF,CAMA,SAEE,kBACF,CAOA,cAGE,+BAAiC,CACjC,aACF,CAeA,QAEE,aAAc,CACd,aAAc,CACd,iBAAkB,CAClB,uBACF,CAEA,IACE,aACF,CAEA,IACE,SACF,CASA,IACE,iBACF,CAUA,sCAKE,mBAAoB,CACpB,cAAe,CACf,gBAAiB,CACjB,QACF,CAOA,aAEE,gBACF,CAOA,cAEE,mBACF,CAMA,gDAIE,yBACF,CAMA,wHAIE,iBAAkB,CAClB,SACF,CAMA,4GAIE,6BACF,CAMA,SACE,0BACF,CASA,OACE,qBAAsB,CACtB,aAAc,CACd,aAAc,CACd,cAAe,CACf,SAAU,CACV,kBACF,CAMA,SACE,uBACF,CAMA,SACE,aACF,CAOA,6BAEE,qBAAsB,CACtB,SACF,CAMA,kFAEE,WACF,CAOA,cACE,4BAA6B,CAC7B,mBACF,CAMA,yCACE,uBACF,CAOA,6BACE,yBAA0B,CAC1B,YACF,CASA,QACE,aACF,CAMA,QACE,iBACF,CAiBA,kBACE,YACF,CCvVA,aAcE,kEACE,uBAOF,WACE,iDAMF,gCACE,wBAEF,qCAEE,uBADA,uBACA,CAEF,SACE,wBAtBA,CCpBJ,iBAOE,6BAEA,mBANA,qBAEA,sBACA,0BAFA,oBAHA,4BAOA,6BANA,mBAOA,CAEF,gBACE,aCPF,KCGE,mHAEA,wGAGA,wBAAyB,CACzB,wBAAyB,CACzB,4BAA6B,CAC7B,yBAA0B,CAC1B,2BAA4B,CAG5B,sDAAuD,CACvD,gDAAiD,CACjD,wDAAyD,CAGzD,0CAA2C,CAC3C,gDAAiD,CACjD,gDAAiD,CAKjD,gCAAiC,CACjC,sCAAuC,CAGvC,2CAA4C,CAG5C,uCAAwC,CChCxC,+FAGA,uBAAwB,CAGxB,iCAAkC,CAClC,kCAAmC,CAEnC,+BAAgC,CAChC,sCAAuC,CACvC,sCAAuC,CACvC,qGAIA,mDAAoD,CAEpD,mCAAoC,CACpC,8CAA+C,CAC/C,gDAAiD,CACjD,kCAAmC,CACnC,6DAA8D,CAG9D,6BAA8B,CAC9B,6BAA8B,CAC9B,+BAAgC,CAChC,kCAAmC,CACnC,kCAAmC,CCPjC,ukBCYA,srCAZF,kaCVA,mLAOA,oTAWA,2UAaA,0CACA,gEACA,0CAGA,gEAUA,yCACA,+DAGA,4CACA,CACA,iEAGA,sGACA,uCACA,4DAGA,sCACA,2DAEA,4CACA,kEACA,oGACA,CAEA,0GACA,+CAGA,+MAOA,+EACA,wCAIA,4DACA,sEACA,kEACA,sEACA,gDAGA,+DACA,0CACA,gEACA,gGACA,CAGA,2DACA,qDAGA,0CACA,8CACA,oDACA,oDL7GF,iCAEA,iEAME,oCKyGA,yDAIA,sCACA,kCACA,sDAGA,0CACA,kEACA,oDAEA,sDAGA,oCACA,oEAIA,CAGA,yDAGA,qDACA,oDAGA,6DAIA,iEAGA,2DAEA,2DL9IE,4DAEA,gEAIF,gEKgGA,gFAIA,oNAOA,qDAEA,gFAIA,4DAIA,oEAMA,yEAIA,6DACA,0DAGA,uDAGA,qDAEA,wDLpII,6DAEA,yDACE,2DAMN,uCAIA,yCACE,8CAGF,sDMjDA,6DAKA,oCAIA,4CACA,kBAGF,sBAMA,2BAME,qCAGA,qCAEA,iCAEA,+BAEA,mCAEA,qCAIA,CACA,gCACA,gDAKA,kCAIA,6BAEA,0CAQA,kCAIF,8BAGE,8BACA,uCAGF,sCAKE,kCAEA,sDAGA,iCACE,CACA,2FAGA,gCACE,CACA,+DCzEJ,wCAEA,sBAEF,yDAEE,mCACA,wDAGA,2GAGA,wIACE,gDAMJ,kCAGE,6BACA,0CAGA,gEACA,8BACA,uCAKA,sCAIA,kCACA,sDACA,iCACA,sCAOA,sDAKE,gGAIE,+CAGN,sBAEE,yCAMA,0BAMA,yLAMA,aACA,MAEF,6BACE,2DAIF,wCAIE,kCAGA,SACA,kCAKA,mBAGA,CAJA,eACA,CAHF,gBAEE,CAWA,mBACA,mBACA,mDAGA,YACA,CACA,kBACA,CAEE,kBAKJ,OAPE,kBAQA,CADF,GACE,iCACA,wCAEA,wBACA,aACA,CAFA,WAEA,GACA,oBACA,CAFA,gBAEA,aACE,+CAIF,UAJE,kCAIF,WACA,iBACA,GAGA,uBACE,CAJF,yBAGA,CACE,iDACA,uCAEA,yDACE,cACA,wDAKN,yDAIE,uBAEF,kBACE,uBAEA,kDAIA,0DAGA,CAHA,oBAGA,0GAYA,aAEA,CAHA,YAGA,4HAKF,+CAGE,sBAEF,WAKE,0CAEA,CALA,qCAGA,CAJA,WAOA,SAIA,2CAJA,qCAIA,CACE,wBACA,OACA,YAEJ,gBACE,gBAIA,+CAKF,CAGE,kDAGA,CANF,8BAGE,CAGA,YAEA,CAdF,2BACE,CAHA,UAEF,CAYE,UAEA,CACA,0CACF,iEAOE,iCACA,8BAGA,wCAIA,wBAKE,0CAKF,CARE,6DAGA,CALF,qBAEE,CASA,YACA,yBAGA,CAEE,cAKN,CAPI,sBAOJ,gCAGE,qBAEA,WACA,aACA,sCAEA,mBACA,6BAGA,uEADA,qBACA,6BAIA,yBACA,qCAEE,UAEA,YACA,sBAEF,8BAGA,CAPE,aACA,WAMF,4BACE,sBACA,WAMJ,uBACE,cAYE,mBAXA,qDAKA,qCAGA,CAEA,YACA,CAHA,2BAEA,CACA,oCAEA,4CACA,uBAIA,oCAEJ,CAFI,cAIF,iBACE,CAHJ,kBAGI,yBAEA,oCAIA,qDAMF,mEAEA,CACE,8CAKA,gCAEA,qCAGA,oCAGE,sBACA,CAJF,WAEE,CAFF,eAEE,SAEA,mBACA,qCACE,aACA,CAFF,YADA,qBACA,WAEE,sBACA,kEAEN,2BAEE,iDAKA,uCAGF,CACE,0DAKA,kBACF,CAFE,sBAGA,mBACA,0BAEJ,yBAII,aADA,WACA,CAMF,UAFE,kBAEF,CAJF,gBACE,CAHE,iBAMF,6CC9ZF,yBACE,WACA,iBAEA,aAFA,iBAEA,6BAEA,kCACA,mBAKA,gCAGA,CARA,QAEA,CAGA,UALA,qBAEA,qDAGA,CALA,OAQA,4BACE,cAGF,2BACE,gCAEJ,CAHE,UAGF,8CAGE,CAHF,UAGE,wCAGA,qBACA,CAFA,UAEA,6CAGA,yCAIA,sBAHA,UAGA,kCACE,OACA,CAFF,KAEE,cAQF,0CACE,CAFF,kBACA,CACE,wEACA,CARA,YACA,CAKF,mBAFF,OAII,eACA,CAJF,iCAJE,cAGJ,CANI,oBAEA,CAKF,SAIE,2BADA,UACA,kBAGF,sCACA,CAFF,WACE,WACA,qCACE,gCACA,2EACA,sDAKJ,aACE,mDAII,CAJJ,6CAII,kEACA,iBACE,iDACA,+CACE,aACA,WADA,+BACA,uEANN,YACE,mDAEE,mBADF,0CACE,CADF,qBACE,0DACA,YACE,4DACA,sEANN,YACE,8CACA,kBADA,UACA,2CACE,2EACA,cACE,kEACA,mEANN,yBACE,4DACA,sBACE,+EAEE,iEACA,qEANN,sCACE,CAGE,iBAHF,gBAGE,qBACE,CAJJ,uBACA,gDACE,wDACA,6DAHF,2CACA,CADA,gBACA,eACE,CAGE,sBANN,8BACE,CAII,iBAFF,4DACA,WACE,YADF,uCACE,6EACA,2BANN,8CACE,kDACA,0CACE,8BACA,yFACE,sBACA,sFALJ,mEACA,sBACE,kEACA,6EACE,uCACA,kEALJ,qGAEE,kEACA,6EACE,uCACA,kEALJ,8CACA,uDACE,sEACA,2EACE,sCACA,iEALJ,mGACA,qCACE,oDACA,0DACE,6GACA,gDAGR,yDCrEA,sEACE,CACA,6GACE,gEACF,iGAIF,wFACE,qDAGA,mGAEE,2CAEF,4FACE,gCACF,wGACE,8DAEE,6FAIA,iJAKN,6GACE,gDAKF,yDACA,qCAGA,6BACA,kBACA,qDAKA,oCAEA,+DAGA,2CAGE,oDAIA,oEAEE,qBAGJ,wDAEE,uCAEF,kEAGA,8CAEA,uDAKA,oCAEA,yDAEE,gEAKF,+CC5FA,0EAGE,CACA,qDCLJ,+DAIE,sCAIA,kEACE,yBACA,2FAMA,gBACA,yGCbF,mBAOA,2MAIA,4HAYA,0DACE,8GAYF,8HAQE,mBAEA,6HAOF,YAGA,mIAME,eACA,CAFF,YAEE,4FAMJ,8BAEE,uBAYA,sCAEE,CAJF,oBAEA,CARA,wCAEA,CAHA,8BACA,CAFA,eACA,CAGA,wCAEA,CAEA,mDAIE,kCACE,6BACA,4CAKJ,kDAIA,eACE,aAGF,8BACE,uDACA,sCACA,cAEA,+BACA,CAFA,eAEA,wCAEF,YACE,iBACA,mCACA,0DAGF,qBAEE,CAFF,kBAEE,+BAIA,yCAEE,qBADA,gBACA,yBAKF,eACA,CAFF,YACE,CACA,iBACA,qDAEA,mDCvIJ,2FAOE,iCACA,CAEA,eACA,CAHA,kBAEA,CAFA,wBAGA,8BACA,eACE,CAFF,YAEE,0BACA,8CAGA,oBACE,oCAGA,kBACE,8DAEA,iBAEN,UACE,8BAIJ,+CAEE,qDAEF,kDAIE,YAEF,CAFE,YAEF,CCjCE,mFAJA,QACA,UAIE,CADF,iBACE,mCAGA,iDACE,+BAGF,wBAEA,mBAKA,6CAEF,CAHE,mBACA,CAEF,kCAIE,CARA,kBACA,CAFF,eASE,YACA,mBAGF,CAJE,UAIF,wCCjCA,oBDmCE,wBCpCJ,uCACE,8BACA,4CACA,oBAGA,2CCAA,6CAGE,CAPF,uBAIA,CDGA,gDACE,6BCVJ,CAWM,2CAEF,CAJA,kCAEE,CDJF,aCLF,gBDKE,uBCMA,gCAGA,gDAGE,wBAGJ,0BAEA,iBACE,aACF,CADE,UACF,uBACE,aACF,oBACE,YACF,4BACE,6CAMA,CAYF,6DAZE,mCAGE,iCASJ,4BAGE,4DADA,+BACA,CAFA,qBAEA,yBACE,aAEF,wBAHA,SAGA,iHACE,2DAKF,CANA,yCACE,CADF,oCAMA,uSAIA,sGACE,oDChEJ,WAEF,yBACE,QACA,eAEA,gBAEE,uCAGA,CALF,iCAKE,uCAGA,0BACA,CACA,oBACA,iCClBJ,gBACE,KAGF,qBACE,YAGF,CAHE,cAGF,gCAEE,mBACA,iEAEA,oCACA,wCAEA,sBACA,WAEA,CAFA,YAEA,8EAEA,mCAFA,iBAEA,6BAIA,wEAKA,sDAIE,CARF,mDAIA,CAIE,cAEF,8CAIA,oBAFE,iBAEF,8CAGE,eAEF,CAFE,YAEF,OAEE,kBAGJ,CAJI,eACA,CAFF,mBAKF,yCCjDE,oBACA,CAFA,iBAEA,uCAKE,iBACA,qCAGA,mBCZJ,CDWI,gBCXJ,6BAEE,eACA,sBAGA,eAEA,sBACA,oDACA,iGAMA,gBAFE,YAEF,8FAME,iJClBF,YACA,gNAUE,6BAEF,oTAcI,kBACF,gHAIA,qBACE,eACF,qDACE,kBACF,6DACE,4BCxCJ,oBAEF,qCAEI,+CAGF,uBACE,uDAGJ,oBAkBE,mDAhBA,+CAaA,CAbA,oBAaA,0FAEE,CAFF,gGAbA,+BAaA,0BAGA,mQAIA,oNAEE,iBAGJ,CAHI,gBADA,gBAIJ,8CAYI,CAZJ,wCAYI,sVACE,iCAGA,uEAHA,QAGA,qXAKJ,iDAGF,CARM,+CACE,iDAIN,CALI,gBAQN,mHACE,gBAGF,2DACE,0EAOA,0EAKA,6EC/EA,iDACA,gCACA,oDAGA,qBACA,oDCFA,cACA,eAEA,yBAGF,sBAEE,iBACA,sNAWA,iBACE,kBACA,wRAgBA,kBAEA,iOAgBA,uCACE,uEAEA,kBAEF,qUAuBE,iDAIJ,CACA,geCxFF,4BAEE,CAQA,6JACA,iDAIA,sEAGA,mDAOF,iDAGE,4DAIA,8CACA,qDAEE,eAFF,cAEE,oBAEF,uBAFE,kCAGA,eACA,iBACA,mBAIA,mDACA,CAHA,uCAEA,CAJA,0CACA,CAIA,gBAJA,gBACA,oBADA,gBAIA,wBAEJ,gBAGE,6BACA,YAHA,iBAGA,gCACA,iEAEA,6CACA,sDACA,0BADA,wBACA,0BACA,oIAIA,mBAFA,YAEA,qBACA,0CAIE,uBAEF,CAHA,yBACE,CAEF,iDACE,mFAKJ,oCACE,CANE,aAKJ,CACE,qEAIA,YAFA,WAEA,CAHA,aACA,CAEA,gBACE,4BACA,sBADA,aACA,gCAMF,oCACA,yDACA,2CAEA,qBAGE,kBAEA,CACA,mCAIF,CARE,YACA,CAOF,iCAEE,CAPA,oBACA,CAQA,oBACE,uDAEJ,sDAGA,CAHA,cAGA,0BACE,oDAIA,oCACA,4BACA,sBAGA,cAEA,oFAGA,sBAEA,yDACE,CAIA,iBAJA,wBAIA,6CAJA,6CAOA,4BAGJ,CAHI,cAGJ,yCAGA,kBACE,CAIA,iDAEA,CATA,YAEF,CACE,4CAGA,kBAIA,wEAEA,wDAIF,kCAOE,iDACA,CARF,WAIE,sCAGA,CANA,2CACA,CAMA,oEARF,iBACE,CACA,qCAMA,iBAuBE,uBAlBF,YAKA,2DALA,uDAKA,CALA,sBAiBA,4CACE,CALA,gRAIF,YACE,UAEN,uBACE,YACA,mCAOE,+CAGA,8BAGF,+CAGA,4BCjNA,SDiNA,qFCjNA,gDAGA,sCACA,qCACA,sDAIF,CAIE,kDAGA,CAPF,0CAOE,kBAEA,kDAEA,CAHA,eACA,CAFA,YACA,CADA,SAIA,mHAIE,CAGA,6CAFA,oCAeE,CAbF,yBACE,qBAEJ,CAGE,oBACA,CAEA,YAFA,2CACF,CACE,uBAEA,mFAEE,CALJ,oBACE,CAEA,UAEE,gCAGF,sDAEA,yCC7CJ,oCAGA,CD6CE,yXAQE,sCCrDJ,wCAGA,oCACE","sources":["webpack:///./node_modules/normalize.css/normalize.css","webpack:///./src/furo/assets/styles/base/_print.sass","webpack:///./src/furo/assets/styles/base/_screen-readers.sass","webpack:///./src/furo/assets/styles/base/_theme.sass","webpack:///./src/furo/assets/styles/variables/_fonts.scss","webpack:///./src/furo/assets/styles/variables/_spacing.scss","webpack:///./src/furo/assets/styles/variables/_icons.scss","webpack:///./src/furo/assets/styles/variables/_admonitions.scss","webpack:///./src/furo/assets/styles/variables/_colors.scss","webpack:///./src/furo/assets/styles/base/_typography.sass","webpack:///./src/furo/assets/styles/_scaffold.sass","webpack:///./src/furo/assets/styles/content/_admonitions.sass","webpack:///./src/furo/assets/styles/content/_api.sass","webpack:///./src/furo/assets/styles/content/_blocks.sass","webpack:///./src/furo/assets/styles/content/_captions.sass","webpack:///./src/furo/assets/styles/content/_code.sass","webpack:///./src/furo/assets/styles/content/_footnotes.sass","webpack:///./src/furo/assets/styles/content/_images.sass","webpack:///./src/furo/assets/styles/content/_indexes.sass","webpack:///./src/furo/assets/styles/content/_lists.sass","webpack:///./src/furo/assets/styles/content/_math.sass","webpack:///./src/furo/assets/styles/content/_misc.sass","webpack:///./src/furo/assets/styles/content/_rubrics.sass","webpack:///./src/furo/assets/styles/content/_sidebar.sass","webpack:///./src/furo/assets/styles/content/_tables.sass","webpack:///./src/furo/assets/styles/content/_target.sass","webpack:///./src/furo/assets/styles/content/_gui-labels.sass","webpack:///./src/furo/assets/styles/components/_footer.sass","webpack:///./src/furo/assets/styles/components/_sidebar.sass","webpack:///./src/furo/assets/styles/components/_table_of_contents.sass","webpack:///./src/furo/assets/styles/_shame.sass"],"sourcesContent":["/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n","// This file contains styles for managing print media.\n\n////////////////////////////////////////////////////////////////////////////////\n// Hide elements not relevant to print media.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Hide icon container.\n .content-icon-container\n display: none !important\n\n // Hide showing header links if hovering over when printing.\n .headerlink\n display: none !important\n\n // Hide mobile header.\n .mobile-header\n display: none !important\n\n // Hide navigation links.\n .related-pages\n display: none !important\n\n////////////////////////////////////////////////////////////////////////////////\n// Tweaks related to decolorization.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Apply a border around code which no longer have a color background.\n .highlight\n border: 0.1pt solid var(--color-foreground-border)\n\n////////////////////////////////////////////////////////////////////////////////\n// Avoid page break in some relevant cases.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n ul, ol, dl, a, table, pre, blockquote\n page-break-inside: avoid\n\n h1, h2, h3, h4, h5, h6, img, figure, caption\n page-break-inside: avoid\n page-break-after: avoid\n\n ul, ol, dl\n page-break-before: avoid\n",".visually-hidden\n position: absolute !important\n width: 1px !important\n height: 1px !important\n padding: 0 !important\n margin: -1px !important\n overflow: hidden !important\n clip: rect(0,0,0,0) !important\n white-space: nowrap !important\n border: 0 !important\n\n:-moz-focusring\n outline: auto\n","// This file serves as the \"skeleton\" of the theming logic.\n//\n// This contains the bulk of the logic for handling dark mode, color scheme\n// toggling and the handling of color-scheme-specific hiding of elements.\n\nbody\n @include fonts\n @include spacing\n @include icons\n @include admonitions\n @include default-admonition(#651fff, \"abstract\")\n @include default-topic(#14B8A6, \"pencil\")\n\n @include colors\n\n.only-light\n display: block !important\nhtml body .only-dark\n display: none !important\n\n// Ignore dark-mode hints if print media.\n@media not print\n // Enable dark-mode, if requested.\n body[data-theme=\"dark\"]\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n // Enable dark mode, unless explicitly told to avoid.\n @media (prefers-color-scheme: dark)\n body:not([data-theme=\"light\"])\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n//\n// Theme toggle presentation\n//\nbody[data-theme=\"auto\"]\n .theme-toggle svg.theme-icon-when-auto\n display: block\n\nbody[data-theme=\"dark\"]\n .theme-toggle svg.theme-icon-when-dark\n display: block\n\nbody[data-theme=\"light\"]\n .theme-toggle svg.theme-icon-when-light\n display: block\n","// Fonts used by this theme.\n//\n// There are basically two things here -- using the system font stack and\n// defining sizes for various elements in %ages. We could have also used `em`\n// but %age is easier to reason about for me.\n\n@mixin fonts {\n // These are adapted from https://systemfontstack.com/\n --font-stack: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n sans-serif, Apple Color Emoji, Segoe UI Emoji;\n --font-stack--monospace: \"SFMono-Regular\", Menlo, Consolas, Monaco,\n Liberation Mono, Lucida Console, monospace;\n\n --font-size--normal: 100%;\n --font-size--small: 87.5%;\n --font-size--small--2: 81.25%;\n --font-size--small--3: 75%;\n --font-size--small--4: 62.5%;\n\n // Sidebar\n --sidebar-caption-font-size: var(--font-size--small--2);\n --sidebar-item-font-size: var(--font-size--small);\n --sidebar-search-input-font-size: var(--font-size--small);\n\n // Table of Contents\n --toc-font-size: var(--font-size--small--3);\n --toc-font-size--mobile: var(--font-size--normal);\n --toc-title-font-size: var(--font-size--small--4);\n\n // Admonitions\n //\n // These aren't defined in terms of %ages, since nesting these is permitted.\n --admonition-font-size: 0.8125rem;\n --admonition-title-font-size: 0.8125rem;\n\n // Code\n --code-font-size: var(--font-size--small--2);\n\n // API\n --api-font-size: var(--font-size--small);\n}\n","// Spacing for various elements on the page\n//\n// If the user wants to tweak things in a certain way, they are permitted to.\n// They also have to deal with the consequences though!\n\n@mixin spacing {\n // Header!\n --header-height: calc(\n var(--sidebar-item-line-height) + 4 * #{var(--sidebar-item-spacing-vertical)}\n );\n --header-padding: 0.5rem;\n\n // Sidebar\n --sidebar-tree-space-above: 1.5rem;\n --sidebar-caption-space-above: 1rem;\n\n --sidebar-item-line-height: 1rem;\n --sidebar-item-spacing-vertical: 0.5rem;\n --sidebar-item-spacing-horizontal: 1rem;\n --sidebar-item-height: calc(\n var(--sidebar-item-line-height) + 2 *#{var(--sidebar-item-spacing-vertical)}\n );\n\n --sidebar-expander-width: var(--sidebar-item-height); // be square\n\n --sidebar-search-space-above: 0.5rem;\n --sidebar-search-input-spacing-vertical: 0.5rem;\n --sidebar-search-input-spacing-horizontal: 0.5rem;\n --sidebar-search-input-height: 1rem;\n --sidebar-search-icon-size: var(--sidebar-search-input-height);\n\n // Table of Contents\n --toc-title-padding: 0.25rem 0;\n --toc-spacing-vertical: 1.5rem;\n --toc-spacing-horizontal: 1.5rem;\n --toc-item-spacing-vertical: 0.4rem;\n --toc-item-spacing-horizontal: 1rem;\n}\n","// Expose theme icons as CSS variables.\n\n$icons: (\n // Adapted from tabler-icons\n // url: https://tablericons.com/\n \"search\":\n url('data:image/svg+xml;charset=utf-8,'),\n // Factored out from mkdocs-material on 24-Aug-2020.\n // url: https://squidfunk.github.io/mkdocs-material/reference/admonitions/\n \"pencil\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"abstract\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"info\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"flame\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"question\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"warning\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"failure\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"spark\":\n url('data:image/svg+xml;charset=utf-8,')\n);\n\n@mixin icons {\n @each $name, $glyph in $icons {\n --icon-#{$name}: #{$glyph};\n }\n}\n","// Admonitions\n\n// Structure of these is:\n// admonition-class: color \"icon-name\";\n//\n// The colors are translated into CSS variables below. The icons are\n// used directly in the main declarations to set the `mask-image` in\n// the title.\n\n// prettier-ignore\n$admonitions: (\n // Each of these has an reST directives for it.\n \"caution\": #ff9100 \"spark\",\n \"warning\": #ff9100 \"warning\",\n \"danger\": #ff5252 \"spark\",\n \"attention\": #ff5252 \"warning\",\n \"error\": #ff5252 \"failure\",\n \"hint\": #00c852 \"question\",\n \"tip\": #00c852 \"info\",\n \"important\": #00bfa5 \"flame\",\n \"note\": #00b0ff \"pencil\",\n \"seealso\": #448aff \"info\",\n \"admonition-todo\": #808080 \"pencil\"\n);\n\n@mixin default-admonition($color, $icon-name) {\n --color-admonition-title: #{$color};\n --color-admonition-title-background: #{rgba($color, 0.2)};\n\n --icon-admonition-default: var(--icon-#{$icon-name});\n}\n\n@mixin default-topic($color, $icon-name) {\n --color-topic-title: #{$color};\n --color-topic-title-background: #{rgba($color, 0.2)};\n\n --icon-topic-default: var(--icon-#{$icon-name});\n}\n\n@mixin admonitions {\n @each $name, $values in $admonitions {\n --color-admonition-title--#{$name}: #{nth($values, 1)};\n --color-admonition-title-background--#{$name}: #{rgba(\n nth($values, 1),\n 0.2\n )};\n }\n}\n","// Colors used throughout this theme.\n//\n// The aim is to give the user more control. Thus, instead of hard-coding colors\n// in various parts of the stylesheet, the approach taken is to define all\n// colors as CSS variables and reusing them in all the places.\n//\n// `colors-dark` depends on `colors` being included at a lower specificity.\n\n@mixin colors {\n --color-problematic: #b30000;\n\n // Base Colors\n --color-foreground-primary: black; // for main text and headings\n --color-foreground-secondary: #5a5c63; // for secondary text\n --color-foreground-muted: #646776; // for muted text\n --color-foreground-border: #878787; // for content borders\n\n --color-background-primary: white; // for content\n --color-background-secondary: #f8f9fb; // for navigation + ToC\n --color-background-hover: #efeff4ff; // for navigation-item hover\n --color-background-hover--transparent: #efeff400;\n --color-background-border: #eeebee; // for UI borders\n --color-background-item: #ccc; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #2962ff;\n --color-brand-content: #2a5adf;\n\n // API documentation\n --color-api-background: var(--color-background-hover--transparent);\n --color-api-background-hover: var(--color-background-hover);\n --color-api-overall: var(--color-foreground-secondary);\n --color-api-name: var(--color-problematic);\n --color-api-pre-name: var(--color-problematic);\n --color-api-paren: var(--color-foreground-secondary);\n --color-api-keyword: var(--color-foreground-primary);\n --color-highlight-on-target: #ffffcc;\n\n // Inline code background\n --color-inline-code-background: var(--color-background-secondary);\n\n // Highlighted text (search)\n --color-highlighted-background: #ddeeff;\n --color-highlighted-text: var(--color-foreground-primary);\n\n // GUI Labels\n --color-guilabel-background: #ddeeff80;\n --color-guilabel-border: #bedaf580;\n --color-guilabel-text: var(--color-foreground-primary);\n\n // Admonitions!\n --color-admonition-background: transparent;\n\n //////////////////////////////////////////////////////////////////////////////\n // Everything below this should be one of:\n // - var(...)\n // - *-gradient(...)\n // - special literal values (eg: transparent, none)\n //////////////////////////////////////////////////////////////////////////////\n\n // Tables\n --color-table-header-background: var(--color-background-secondary);\n --color-table-border: var(--color-background-border);\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: transparent;\n --color-card-marginals-background: var(--color-background-secondary);\n\n // Header\n --color-header-background: var(--color-background-primary);\n --color-header-border: var(--color-background-border);\n --color-header-text: var(--color-foreground-primary);\n\n // Sidebar (left)\n --color-sidebar-background: var(--color-background-secondary);\n --color-sidebar-background-border: var(--color-background-border);\n\n --color-sidebar-brand-text: var(--color-foreground-primary);\n --color-sidebar-caption-text: var(--color-foreground-muted);\n --color-sidebar-link-text: var(--color-foreground-secondary);\n --color-sidebar-link-text--top-level: var(--color-brand-primary);\n\n --color-sidebar-item-background: var(--color-sidebar-background);\n --color-sidebar-item-background--current: var(\n --color-sidebar-item-background\n );\n --color-sidebar-item-background--hover: linear-gradient(\n 90deg,\n var(--color-background-hover--transparent) 0%,\n var(--color-background-hover) var(--sidebar-item-spacing-horizontal),\n var(--color-background-hover) 100%\n );\n\n --color-sidebar-item-expander-background: transparent;\n --color-sidebar-item-expander-background--hover: var(\n --color-background-hover\n );\n\n --color-sidebar-search-text: var(--color-foreground-primary);\n --color-sidebar-search-background: var(--color-background-secondary);\n --color-sidebar-search-background--focus: var(--color-background-primary);\n --color-sidebar-search-border: var(--color-background-border);\n --color-sidebar-search-icon: var(--color-foreground-muted);\n\n // Table of Contents (right)\n --color-toc-background: var(--color-background-primary);\n --color-toc-title-text: var(--color-foreground-muted);\n --color-toc-item-text: var(--color-foreground-secondary);\n --color-toc-item-text--hover: var(--color-foreground-primary);\n --color-toc-item-text--active: var(--color-brand-primary);\n\n // Actual page contents\n --color-content-foreground: var(--color-foreground-primary);\n --color-content-background: transparent;\n\n // Links\n --color-link: var(--color-brand-content);\n --color-link--hover: var(--color-brand-content);\n --color-link-underline: var(--color-background-border);\n --color-link-underline--hover: var(--color-foreground-border);\n}\n\n@mixin colors-dark {\n --color-problematic: #ee5151;\n\n // Base Colors\n --color-foreground-primary: #ffffffcc; // for main text and headings\n --color-foreground-secondary: #9ca0a5; // for secondary text\n --color-foreground-muted: #81868d; // for muted text\n --color-foreground-border: #666666; // for content borders\n\n --color-background-primary: #131416; // for content\n --color-background-secondary: #1a1c1e; // for navigation + ToC\n --color-background-hover: #1e2124ff; // for navigation-item hover\n --color-background-hover--transparent: #1e212400;\n --color-background-border: #303335; // for UI borders\n --color-background-item: #444; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #2b8cee;\n --color-brand-content: #368ce2;\n\n // Highlighted text (search)\n --color-highlighted-background: #083563;\n\n // GUI Labels\n --color-guilabel-background: #08356380;\n --color-guilabel-border: #13395f80;\n\n // API documentation\n --color-api-keyword: var(--color-foreground-secondary);\n --color-highlight-on-target: #333300;\n\n // Admonitions\n --color-admonition-background: #18181a;\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: #18181a;\n --color-card-marginals-background: var(--color-background-hover);\n}\n","// This file contains the styling for making the content throughout the page,\n// including fonts, paragraphs, headings and spacing among these elements.\n\nbody\n font-family: var(--font-stack)\npre,\ncode,\nkbd,\nsamp\n font-family: var(--font-stack--monospace)\n\n// Make fonts look slightly nicer.\nbody\n -webkit-font-smoothing: antialiased\n -moz-osx-font-smoothing: grayscale\n\n// Line height from Bootstrap 4.1\narticle\n line-height: 1.5\n\n//\n// Headings\n//\nh1,\nh2,\nh3,\nh4,\nh5,\nh6\n line-height: 1.25\n font-weight: bold\n\n border-radius: 0.5rem\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n margin-left: -0.5rem\n margin-right: -0.5rem\n padding-left: 0.5rem\n padding-right: 0.5rem\n\n + p\n margin-top: 0\n\nh1\n font-size: 2.5em\n margin-top: 1.75rem\n margin-bottom: 1rem\nh2\n font-size: 2em\n margin-top: 1.75rem\nh3\n font-size: 1.5em\nh4\n font-size: 1.25em\nh5\n font-size: 1.125em\nh6\n font-size: 1em\n\nsmall\n opacity: 75%\n font-size: 80%\n\n// Paragraph\np\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n\n// Horizontal rules\nhr.docutils\n height: 1px\n padding: 0\n margin: 2rem 0\n background-color: var(--color-background-border)\n border: 0\n\n.centered\n text-align: center\n\n// Links\na\n text-decoration: underline\n\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n &:hover\n color: var(--color-link--hover)\n text-decoration-color: var(--color-link-underline--hover)\n &.muted-link\n color: inherit\n &:hover\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline--hover)\n","// This file contains the styles for the overall layouting of the documentation\n// skeleton, including the responsive changes as well as sidebar toggles.\n//\n// This is implemented as a mobile-last design, which isn't ideal, but it is\n// reasonably good-enough and I got pretty tired by the time I'd finished this\n// to move the rules around to fix this. Shouldn't take more than 3-4 hours,\n// if you know what you're doing tho.\n\n// HACK: Not all browsers account for the scrollbar width in media queries.\n// This results in horizontal scrollbars in the breakpoint where we go\n// from displaying everything to hiding the ToC. We accomodate for this by\n// adding a bit of padding to the TOC drawer, disabling the horizontal\n// scrollbar and allowing the scrollbars to cover the padding.\n// https://www.456bereastreet.com/archive/201301/media_query_width_and_vertical_scrollbars/\n\n// HACK: Always having the scrollbar visible, prevents certain browsers from\n// causing the content to stutter horizontally between taller-than-viewport and\n// not-taller-than-viewport pages.\n\nhtml\n overflow-x: hidden\n overflow-y: scroll\n scroll-behavior: smooth\n\n.sidebar-scroll, .toc-scroll, article[role=main] *\n // Override Firefox scrollbar style\n scrollbar-width: thin\n scrollbar-color: var(--color-foreground-border) transparent\n\n // Override Chrome scrollbar styles\n &::-webkit-scrollbar\n width: 0.25rem\n height: 0.25rem\n &::-webkit-scrollbar-thumb\n background-color: var(--color-foreground-border)\n border-radius: 0.125rem\n\n//\n// Overalls\n//\nhtml,\nbody\n height: 100%\n color: var(--color-foreground-primary)\n background: var(--color-background-primary)\n\narticle\n color: var(--color-content-foreground)\n background: var(--color-content-background)\n overflow-wrap: break-word\n\n.page\n display: flex\n // fill the viewport for pages with little content.\n min-height: 100%\n\n.mobile-header\n width: 100%\n height: var(--header-height)\n background-color: var(--color-header-background)\n color: var(--color-header-text)\n border-bottom: 1px solid var(--color-header-border)\n\n // Looks like sub-script/super-script have this, and we need this to\n // be \"on top\" of those.\n z-index: 10\n\n // We don't show the header on large screens.\n display: none\n\n // Add shadow when scrolled\n &.scrolled\n border-bottom: none\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.4rem rgba(0, 0, 0, 0.2)\n\n .header-center\n a\n color: var(--color-header-text)\n text-decoration: none\n\n.main\n display: flex\n flex: 1\n\n// Sidebar (left) also covers the entire left portion of screen.\n.sidebar-drawer\n box-sizing: border-box\n\n border-right: 1px solid var(--color-sidebar-background-border)\n background: var(--color-sidebar-background)\n\n display: flex\n justify-content: flex-end\n // These next two lines took me two days to figure out.\n width: calc((100% - #{$full-width}) / 2 + #{$sidebar-width})\n min-width: $sidebar-width\n\n// Scroll-along sidebars\n.sidebar-container,\n.toc-drawer\n box-sizing: border-box\n width: $sidebar-width\n\n.toc-drawer\n background: var(--color-toc-background)\n // See HACK described on top of this document\n padding-right: 1rem\n\n.sidebar-sticky,\n.toc-sticky\n position: sticky\n top: 0\n height: min(100%, 100vh)\n height: 100vh\n\n display: flex\n flex-direction: column\n\n.sidebar-scroll,\n.toc-scroll\n flex-grow: 1\n flex-shrink: 1\n\n overflow: auto\n scroll-behavior: smooth\n\n// Central items.\n.content\n padding: 0 $content-padding\n width: $content-width\n\n display: flex\n flex-direction: column\n justify-content: space-between\n\n.icon\n display: inline-block\n height: 1rem\n width: 1rem\n svg\n width: 100%\n height: 100%\n\n//\n// Accommodate announcement banner\n//\n.announcement\n background-color: var(--color-announcement-background)\n color: var(--color-announcement-text)\n\n height: var(--header-height)\n display: flex\n align-items: center\n overflow-x: auto\n & + .page\n min-height: calc(100% - var(--header-height))\n\n.announcement-content\n box-sizing: border-box\n padding: 0.5rem\n min-width: 100%\n white-space: nowrap\n text-align: center\n\n a\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-announcement-text)\n\n &:hover\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-link--hover)\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for theme\n////////////////////////////////////////////////////////////////////////////////\n.no-js .theme-toggle-container // don't show theme toggle if there's no JS\n display: none\n\n.theme-toggle-container\n vertical-align: middle\n\n.theme-toggle\n cursor: pointer\n border: none\n padding: 0\n background: transparent\n\n.theme-toggle svg\n vertical-align: middle\n height: 1rem\n width: 1rem\n color: var(--color-foreground-primary)\n display: none\n\n.theme-toggle-header\n float: left\n padding: 1rem 0.5rem\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for elements\n////////////////////////////////////////////////////////////////////////////////\n.toc-overlay-icon, .nav-overlay-icon\n display: none\n cursor: pointer\n\n .icon\n color: var(--color-foreground-secondary)\n height: 1rem\n width: 1rem\n\n.toc-header-icon, .nav-overlay-icon\n // for when we set display: flex\n justify-content: center\n align-items: center\n\n.toc-content-icon\n height: 1.5rem\n width: 1.5rem\n\n.content-icon-container\n float: right\n display: flex\n margin-top: 1.5rem\n margin-left: 1rem\n margin-bottom: 1rem\n gap: 0.5rem\n\n .edit-this-page svg\n color: inherit\n height: 1rem\n width: 1rem\n\n.sidebar-toggle\n position: absolute\n display: none\n// \n.sidebar-toggle[name=\"__toc\"]\n left: 20px\n.sidebar-toggle:checked\n left: 40px\n// \n\n.overlay\n position: fixed\n top: 0\n width: 0\n height: 0\n\n transition: width 0ms, height 0ms, opacity 250ms ease-out\n\n opacity: 0\n background-color: rgba(0, 0, 0, 0.54)\n.sidebar-overlay\n z-index: 20\n.toc-overlay\n z-index: 40\n\n// Keep things on top and smooth.\n.sidebar-drawer\n z-index: 30\n transition: left 250ms ease-in-out\n.toc-drawer\n z-index: 50\n transition: right 250ms ease-in-out\n\n// Show the Sidebar\n#__navigation:checked\n & ~ .sidebar-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .sidebar-drawer\n top: 0\n left: 0\n // Show the toc sidebar\n#__toc:checked\n & ~ .toc-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .toc-drawer\n top: 0\n right: 0\n\n////////////////////////////////////////////////////////////////////////////////\n// Back to top\n////////////////////////////////////////////////////////////////////////////////\n.back-to-top\n text-decoration: none\n\n display: none\n position: fixed\n left: 0\n top: 1rem\n padding: 0.5rem\n padding-right: 0.75rem\n border-radius: 1rem\n font-size: 0.8125rem\n\n background: var(--color-background-primary)\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), #6b728080 0px 0px 1px 0px\n\n z-index: 10\n\n margin-left: 50%\n transform: translateX(-50%)\n svg\n height: 1rem\n width: 1rem\n fill: currentColor\n display: inline-block\n\n span\n margin-left: 0.25rem\n\n .show-back-to-top &\n display: flex\n align-items: center\n\n////////////////////////////////////////////////////////////////////////////////\n// Responsive layouting\n////////////////////////////////////////////////////////////////////////////////\n// Make things a bit bigger on bigger screens.\n@media (min-width: $full-width + $sidebar-width)\n html\n font-size: 110%\n\n@media (max-width: $full-width)\n // Collapse \"toc\" into the icon.\n .toc-content-icon\n display: flex\n .toc-drawer\n position: fixed\n height: 100vh\n top: 0\n right: -$sidebar-width\n border-left: 1px solid var(--color-background-muted)\n .toc-tree\n border-left: none\n font-size: var(--toc-font-size--mobile)\n\n // Accomodate for a changed content width.\n .sidebar-drawer\n width: calc((100% - #{$full-width - $sidebar-width}) / 2 + #{$sidebar-width})\n\n@media (max-width: $full-width - $sidebar-width)\n // Collapse \"navigation\".\n .nav-overlay-icon\n display: flex\n .sidebar-drawer\n position: fixed\n height: 100vh\n width: $sidebar-width\n\n top: 0\n left: -$sidebar-width\n\n // Swap which icon is visible.\n .toc-header-icon\n display: flex\n .toc-content-icon, .theme-toggle-content\n display: none\n .theme-toggle-header\n display: block\n\n // Show the header.\n .mobile-header\n position: sticky\n top: 0\n display: flex\n justify-content: space-between\n align-items: center\n\n .header-left,\n .header-right\n display: flex\n height: var(--header-height)\n padding: 0 var(--header-padding)\n label\n height: 100%\n width: 100%\n user-select: none\n\n .nav-overlay-icon .icon,\n .theme-toggle svg\n height: 1.25rem\n width: 1.25rem\n\n // Add a scroll margin for the content\n :target\n scroll-margin-top: var(--header-height)\n\n // Show back-to-top below the header\n .back-to-top\n top: calc(var(--header-height) + 0.5rem)\n\n // Center the page, and accommodate for the header.\n .page\n flex-direction: column\n justify-content: center\n .content\n margin-left: auto\n margin-right: auto\n\n@media (max-width: $content-width + 2* $content-padding)\n // Content should respect window limits.\n .content\n width: 100%\n overflow-x: auto\n\n@media (max-width: $content-width)\n .content\n padding: 0 $content-padding--small\n // Don't float sidebars to the right.\n article aside.sidebar\n float: none\n width: 100%\n margin: 1rem 0\n","//\n// The design here is strongly inspired by mkdocs-material.\n.admonition, .topic\n margin: 1rem auto\n padding: 0 0.5rem 0.5rem 0.5rem\n\n background: var(--color-admonition-background)\n\n border-radius: 0.2rem\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n font-size: var(--admonition-font-size)\n\n overflow: hidden\n page-break-inside: avoid\n\n // First element should have no margin, since the title has it.\n > :nth-child(2)\n margin-top: 0\n\n // Last item should have no margin, since we'll control that w/ padding\n > :last-child\n margin-bottom: 0\n\n.admonition p.admonition-title,\np.topic-title\n position: relative\n margin: 0 -0.5rem 0.5rem\n padding-left: 2rem\n padding-right: .5rem\n padding-top: .4rem\n padding-bottom: .4rem\n\n font-weight: 500\n font-size: var(--admonition-title-font-size)\n line-height: 1.3\n\n // Our fancy icon\n &::before\n content: \"\"\n position: absolute\n left: 0.5rem\n width: 1rem\n height: 1rem\n\n// Default styles\np.admonition-title\n background-color: var(--color-admonition-title-background)\n &::before\n background-color: var(--color-admonition-title)\n mask-image: var(--icon-admonition-default)\n mask-repeat: no-repeat\n\np.topic-title\n background-color: var(--color-topic-title-background)\n &::before\n background-color: var(--color-topic-title)\n mask-image: var(--icon-topic-default)\n mask-repeat: no-repeat\n\n//\n// Variants\n//\n.admonition\n border-left: 0.2rem solid var(--color-admonition-title)\n\n @each $type, $value in $admonitions\n &.#{$type}\n border-left-color: var(--color-admonition-title--#{$type})\n > .admonition-title\n background-color: var(--color-admonition-title-background--#{$type})\n &::before\n background-color: var(--color-admonition-title--#{$type})\n mask-image: var(--icon-#{nth($value, 2)})\n\n.admonition-todo > .admonition-title\n text-transform: uppercase\n","// This file stylizes the API documentation (stuff generated by autodoc). It's\n// deeply nested due to how autodoc structures the HTML without enough classes\n// to select the relevant items.\n\n// API docs!\ndl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)\n // Tweak the spacing of all the things!\n dd\n margin-left: 2rem\n > :first-child\n margin-top: 0.125rem\n > :last-child\n margin-bottom: 0.75rem\n\n // This is used for the arguments\n .field-list\n margin-bottom: 0.75rem\n\n // \"Headings\" (like \"Parameters\" and \"Return\")\n > dt\n text-transform: uppercase\n font-size: var(--font-size--small)\n\n dd:empty\n margin-bottom: 0.5rem\n dd > ul\n margin-left: -1.2rem\n > li\n > p:nth-child(2)\n margin-top: 0\n // When the last-empty-paragraph follows a paragraph, it doesn't need\n // to augument the existing spacing.\n > p + p:last-child:empty\n margin-top: 0\n margin-bottom: 0\n\n // Colorize the elements\n > dt\n color: var(--color-api-overall)\n\n.sig:not(.sig-inline)\n font-weight: bold\n\n font-size: var(--api-font-size)\n font-family: var(--font-stack--monospace)\n\n margin-left: -0.25rem\n margin-right: -0.25rem\n padding-top: 0.25rem\n padding-bottom: 0.25rem\n padding-right: 0.5rem\n\n // These are intentionally em, to properly match the font size.\n padding-left: 3em\n text-indent: -2.5em\n\n border-radius: 0.25rem\n\n background: var(--color-api-background)\n transition: background 100ms ease-out\n\n &:hover\n background: var(--color-api-background-hover)\n\n // adjust the size of the [source] link on the right.\n a.reference\n .viewcode-link\n font-weight: normal\n width: 3.5rem\n\nem.property\n font-style: normal\n &:first-child\n color: var(--color-api-keyword)\n.sig-name\n color: var(--color-api-name)\n.sig-prename\n font-weight: normal\n color: var(--color-api-pre-name)\n.sig-paren\n color: var(--color-api-paren)\n.sig-param\n font-style: normal\n\n.versionmodified\n font-style: italic\ndiv.versionadded, div.versionchanged, div.deprecated\n p\n margin-top: 0.125rem\n margin-bottom: 0.125rem\n\n// Align the [docs] and [source] to the right.\n.viewcode-link, .viewcode-back\n float: right\n text-align: right\n",".line-block\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n .line-block\n margin-top: 0rem\n margin-bottom: 0rem\n padding-left: 1rem\n","// Captions\narticle p.caption,\ntable > caption,\n.code-block-caption\n font-size: var(--font-size--small)\n text-align: center\n\n// Caption above a TOCTree\n.toctree-wrapper.compound\n .caption, :not(.caption) > .caption-text\n font-size: var(--font-size--small)\n text-transform: uppercase\n\n text-align: initial\n margin-bottom: 0\n\n > ul\n margin-top: 0\n margin-bottom: 0\n","// Inline code\ncode.literal, .sig-inline\n background: var(--color-inline-code-background)\n border-radius: 0.2em\n // Make the font smaller, and use padding to recover.\n font-size: var(--font-size--small--2)\n padding: 0.1em 0.2em\n\n pre.literal-block &\n font-size: inherit\n padding: 0\n\n p &\n border: 1px solid var(--color-background-border)\n\n.sig-inline\n font-family: var(--font-stack--monospace)\n\n// Code and Literal Blocks\n$code-spacing-vertical: 0.625rem\n$code-spacing-horizontal: 0.875rem\n\n// Wraps every literal block + line numbers.\ndiv[class*=\" highlight-\"],\ndiv[class^=\"highlight-\"]\n margin: 1em 0\n display: flex\n\n .table-wrapper\n margin: 0\n padding: 0\n\npre\n margin: 0\n padding: 0\n overflow: auto\n\n // Needed to have more specificity than pygments' \"pre\" selector. :(\n article[role=\"main\"] .highlight &\n line-height: 1.5\n\n &.literal-block,\n .highlight &\n font-size: var(--code-font-size)\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n // Make it look like all the other blocks.\n &.literal-block\n margin-top: 1rem\n margin-bottom: 1rem\n\n border-radius: 0.2rem\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n\n// All code is always contained in this.\n.highlight\n width: 100%\n border-radius: 0.2rem\n\n // Make line numbers and prompts un-selectable.\n .gp, span.linenos\n user-select: none\n pointer-events: none\n\n // Expand the line-highlighting.\n .hll\n display: block\n margin-left: -$code-spacing-horizontal\n margin-right: -$code-spacing-horizontal\n padding-left: $code-spacing-horizontal\n padding-right: $code-spacing-horizontal\n\n/* Make code block captions be nicely integrated */\n.code-block-caption\n display: flex\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n border-radius: 0.25rem\n border-bottom-left-radius: 0\n border-bottom-right-radius: 0\n font-weight: 300\n border-bottom: 1px solid\n\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n border-color: var(--color-background-border)\n\n + div[class]\n margin-top: 0\n pre\n border-top-left-radius: 0\n border-top-right-radius: 0\n\n// When `html_codeblock_linenos_style` is table.\n.highlighttable\n width: 100%\n display: block\n tbody\n display: block\n\n tr\n display: flex\n\n // Line numbers\n td.linenos\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n padding: $code-spacing-vertical $code-spacing-horizontal\n padding-right: 0\n border-top-left-radius: 0.2rem\n border-bottom-left-radius: 0.2rem\n\n .linenodiv\n padding-right: $code-spacing-horizontal\n font-size: var(--code-font-size)\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n\n // Actual code\n td.code\n padding: 0\n display: block\n flex: 1\n overflow: hidden\n\n .highlight\n border-top-left-radius: 0\n border-bottom-left-radius: 0\n\n// When `html_codeblock_linenos_style` is inline.\n.highlight\n span.linenos\n display: inline-block\n padding-left: 0\n padding-right: $code-spacing-horizontal\n margin-right: $code-spacing-horizontal\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n","// Inline Footnote Reference\n.footnote-reference\n font-size: var(--font-size--small--4)\n vertical-align: super\n\n// Definition list, listing the content of each note.\n// docutils <= 0.17\ndl.footnote.brackets\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\n display: grid\n grid-template-columns: max-content auto\n dt\n margin: 0\n > .fn-backref\n margin-left: 0.25rem\n\n &:after\n content: \":\"\n\n .brackets\n &:before\n content: \"[\"\n &:after\n content: \"]\"\n\n dd\n margin: 0\n padding: 0 1rem\n\n// docutils >= 0.18\naside.footnote\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\naside.footnote > span,\ndiv.citation > span\n float: left\n font-weight: 500\n padding-right: 0.25rem\n\naside.footnote > p,\ndiv.citation > p\n margin-left: 2rem\n","//\n// Figures\n//\nimg\n box-sizing: border-box\n max-width: 100%\n height: auto\n\narticle\n figure, .figure\n border-radius: 0.2rem\n\n margin: 0\n :last-child\n margin-bottom: 0\n\n .align-left\n float: left\n clear: left\n margin: 0 1rem 1rem\n\n .align-right\n float: right\n clear: right\n margin: 0 1rem 1rem\n\n .align-default,\n .align-center\n display: block\n text-align: center\n margin-left: auto\n margin-right: auto\n\n // WELL, table needs to be stylised like a table.\n table.align-default\n display: table\n text-align: initial\n",".genindex-jumpbox, .domainindex-jumpbox\n border-top: 1px solid var(--color-background-border)\n border-bottom: 1px solid var(--color-background-border)\n padding: 0.25rem\n\n.genindex-section, .domainindex-section\n h2\n margin-top: 0.75rem\n margin-bottom: 0.5rem\n ul\n margin-top: 0\n margin-bottom: 0\n","ul,\nol\n padding-left: 1.2rem\n\n // Space lists out like paragraphs\n margin-top: 1rem\n margin-bottom: 1rem\n // reduce margins within li.\n li\n > p:first-child\n margin-top: 0.25rem\n margin-bottom: 0.25rem\n\n > p:last-child\n margin-top: 0.25rem\n\n > ul,\n > ol\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n\nol\n &.arabic\n list-style: decimal\n &.loweralpha\n list-style: lower-alpha\n &.upperalpha\n list-style: upper-alpha\n &.lowerroman\n list-style: lower-roman\n &.upperroman\n list-style: upper-roman\n\n// Don't space lists out when they're \"simple\" or in a `.. toctree::`\n.simple,\n.toctree-wrapper\n li\n > ul,\n > ol\n margin-top: 0\n margin-bottom: 0\n\n// Definition Lists\n.field-list,\n.option-list,\ndl:not([class]),\ndl.simple,\ndl.footnote,\ndl.glossary\n dt\n font-weight: 500\n margin-top: 0.25rem\n + dt\n margin-top: 0\n\n .classifier::before\n content: \":\"\n margin-left: 0.2rem\n margin-right: 0.2rem\n\n dd\n > p:first-child,\n ul\n margin-top: 0.125rem\n\n ul\n margin-bottom: 0.125rem\n",".math-wrapper\n width: 100%\n overflow-x: auto\n\ndiv.math\n position: relative\n text-align: center\n\n .headerlink,\n &:focus .headerlink\n display: none\n\n &:hover .headerlink\n display: inline-block\n\n span.eqno\n position: absolute\n right: 0.5rem\n top: 50%\n transform: translate(0, -50%)\n z-index: 1\n","// Abbreviations\nabbr[title]\n cursor: help\n\n// \"Problematic\" content, as identified by Sphinx\n.problematic\n color: var(--color-problematic)\n\n// Keyboard / Mouse \"instructions\"\nkbd:not(.compound)\n margin: 0 0.2rem\n padding: 0 0.2rem\n border-radius: 0.2rem\n border: 1px solid var(--color-foreground-border)\n color: var(--color-foreground-primary)\n vertical-align: text-bottom\n\n font-size: var(--font-size--small--3)\n display: inline-block\n\n box-shadow: 0 0.0625rem 0 rgba(0, 0, 0, 0.2), inset 0 0 0 0.125rem var(--color-background-primary)\n\n background-color: var(--color-background-secondary)\n\n// Blockquote\nblockquote\n border-left: 4px solid var(--color-background-border)\n background: var(--color-background-secondary)\n\n margin-left: 0\n margin-right: 0\n padding: 0.5rem 1rem\n\n .attribution\n font-weight: 600\n text-align: right\n\n &.pull-quote,\n &.highlights\n font-size: 1.25em\n\n &.epigraph,\n &.pull-quote\n border-left-width: 0\n border-radius: 0.5rem\n\n &.highlights\n border-left-width: 0\n background: transparent\n\n// Center align embedded-in-text images\np .reference img\n vertical-align: middle\n","p.rubric\n line-height: 1.25\n font-weight: bold\n font-size: 1.125em\n\n // For Numpy-style documentation that's got rubrics within it.\n // https://github.com/pradyunsg/furo/discussions/505\n dd &\n line-height: inherit\n font-weight: inherit\n\n font-size: var(--font-size--small)\n text-transform: uppercase\n","article .sidebar\n float: right\n clear: right\n width: 30%\n\n margin-left: 1rem\n margin-right: 0\n\n border-radius: 0.2rem\n background-color: var(--color-background-secondary)\n border: var(--color-background-border) 1px solid\n\n > *\n padding-left: 1rem\n padding-right: 1rem\n\n > ul, > ol // lists need additional padding, because bullets.\n padding-left: 2.2rem\n\n .sidebar-title\n margin: 0\n padding: 0.5rem 1rem\n border-bottom: var(--color-background-border) 1px solid\n\n font-weight: 500\n\n// TODO: subtitle\n// TODO: dedicated variables?\n",".table-wrapper\n width: 100%\n overflow-x: auto\n margin-top: 1rem\n margin-bottom: 0.5rem\n padding: 0.2rem 0.2rem 0.75rem\n\ntable.docutils\n border-radius: 0.2rem\n border-spacing: 0\n border-collapse: collapse\n\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n th\n background: var(--color-table-header-background)\n\n td,\n th\n // Space things out properly\n padding: 0 0.25rem\n\n // Get the borders looking just-right.\n border-left: 1px solid var(--color-table-border)\n border-right: 1px solid var(--color-table-border)\n border-bottom: 1px solid var(--color-table-border)\n\n p\n margin: 0.25rem\n\n &:first-child\n border-left: none\n &:last-child\n border-right: none\n\n // MyST-parser tables set these classes for control of column alignment\n &.text-left\n text-align: left\n &.text-right\n text-align: right\n &.text-center\n text-align: center\n",":target\n scroll-margin-top: 0.5rem\n\n@media (max-width: $full-width - $sidebar-width)\n :target\n scroll-margin-top: calc(0.5rem + var(--header-height))\n\n // When a heading is selected\n section > span:target\n scroll-margin-top: calc(0.8rem + var(--header-height))\n\n// Permalinks\n.headerlink\n font-weight: 100\n user-select: none\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\ndl dt,\np.caption,\nfigcaption p,\ntable > caption,\n.code-block-caption\n > .headerlink\n margin-left: 0.5rem\n visibility: hidden\n &:hover > .headerlink\n visibility: visible\n\n // Don't change to link-like, if someone adds the contents directive.\n > .toc-backref\n color: inherit\n text-decoration-line: none\n\n// Figure and table captions are special.\nfigure:hover > figcaption > p > .headerlink,\ntable:hover > caption > .headerlink\n visibility: visible\n\n:target >, // Regular section[id] style anchors\nspan:target ~ // Non-regular span[id] style \"extra\" anchors\n h1,\n h2,\n h3,\n h4,\n h5,\n h6\n &:nth-of-type(1)\n background-color: var(--color-highlight-on-target)\n // .headerlink\n // visibility: visible\n code.literal\n background-color: transparent\n\ntable:target > caption,\nfigure:target\n background-color: var(--color-highlight-on-target)\n\n// Inline page contents\n.this-will-duplicate-information-and-it-is-still-useful-here li :target\n background-color: var(--color-highlight-on-target)\n\n// Code block permalinks\n.literal-block-wrapper:target .code-block-caption\n background-color: var(--color-highlight-on-target)\n\n// When a definition list item is selected\n//\n// There isn't really an alternative to !important here, due to the\n// high-specificity of API documentation's selector.\ndt:target\n background-color: var(--color-highlight-on-target) !important\n\n// When a footnote reference is selected\n.footnote > dt:target + dd,\n.footnote-reference:target\n background-color: var(--color-highlight-on-target)\n",".guilabel\n background-color: var(--color-guilabel-background)\n border: 1px solid var(--color-guilabel-border)\n color: var(--color-guilabel-text)\n\n padding: 0 0.3em\n border-radius: 0.5em\n font-size: 0.9em\n","// This file contains the styles used for stylizing the footer that's shown\n// below the content.\n\nfooter\n font-size: var(--font-size--small)\n display: flex\n flex-direction: column\n\n margin-top: 2rem\n\n// Bottom of page information\n.bottom-of-page\n display: flex\n align-items: center\n justify-content: space-between\n\n margin-top: 1rem\n padding-top: 1rem\n padding-bottom: 1rem\n\n color: var(--color-foreground-secondary)\n border-top: 1px solid var(--color-background-border)\n\n line-height: 1.5\n\n @media (max-width: $content-width)\n text-align: center\n flex-direction: column-reverse\n gap: 0.25rem\n\n .left-details\n font-size: var(--font-size--small)\n\n .right-details\n display: flex\n flex-direction: column\n gap: 0.25rem\n text-align: right\n\n .icons\n display: flex\n justify-content: flex-end\n gap: 0.25rem\n font-size: 1rem\n\n a\n text-decoration: none\n\n svg,\n img\n font-size: 1.125rem\n height: 1em\n width: 1em\n\n// Next/Prev page information\n.related-pages\n a\n display: flex\n align-items: center\n\n text-decoration: none\n &:hover .page-info .title\n text-decoration: underline\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n svg.furo-related-icon,\n svg.furo-related-icon > use\n flex-shrink: 0\n\n color: var(--color-foreground-border)\n\n width: 0.75rem\n height: 0.75rem\n margin: 0 0.5rem\n\n &.next-page\n max-width: 50%\n\n float: right\n clear: right\n text-align: right\n\n &.prev-page\n max-width: 50%\n\n float: left\n clear: left\n\n svg\n transform: rotate(180deg)\n\n.page-info\n display: flex\n flex-direction: column\n overflow-wrap: anywhere\n\n .next-page &\n align-items: flex-end\n\n .context\n display: flex\n align-items: center\n\n padding-bottom: 0.1rem\n\n color: var(--color-foreground-muted)\n font-size: var(--font-size--small)\n text-decoration: none\n","// This file contains the styles for the contents of the left sidebar, which\n// contains the navigation tree, logo, search etc.\n\n////////////////////////////////////////////////////////////////////////////////\n// Brand on top of the scrollable tree.\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-brand\n display: flex\n flex-direction: column\n flex-shrink: 0\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n text-decoration: none\n\n.sidebar-brand-text\n color: var(--color-sidebar-brand-text)\n overflow-wrap: break-word\n margin: var(--sidebar-item-spacing-vertical) 0\n font-size: 1.5rem\n\n.sidebar-logo-container\n margin: var(--sidebar-item-spacing-vertical) 0\n\n.sidebar-logo\n margin: 0 auto\n display: block\n max-width: 100%\n\n////////////////////////////////////////////////////////////////////////////////\n// Search\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-search-container\n display: flex\n align-items: center\n margin-top: var(--sidebar-search-space-above)\n\n position: relative\n\n background: var(--color-sidebar-search-background)\n &:hover,\n &:focus-within\n background: var(--color-sidebar-search-background--focus)\n\n &::before\n content: \"\"\n position: absolute\n left: var(--sidebar-item-spacing-horizontal)\n width: var(--sidebar-search-icon-size)\n height: var(--sidebar-search-icon-size)\n\n background-color: var(--color-sidebar-search-icon)\n mask-image: var(--icon-search)\n\n.sidebar-search\n box-sizing: border-box\n\n border: none\n border-top: 1px solid var(--color-sidebar-search-border)\n border-bottom: 1px solid var(--color-sidebar-search-border)\n\n padding-top: var(--sidebar-search-input-spacing-vertical)\n padding-bottom: var(--sidebar-search-input-spacing-vertical)\n padding-right: var(--sidebar-search-input-spacing-horizontal)\n padding-left: calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size))\n\n width: 100%\n\n color: var(--color-sidebar-search-foreground)\n background: transparent\n z-index: 10\n\n &:focus\n outline: none\n\n &::placeholder\n font-size: var(--sidebar-search-input-font-size)\n\n//\n// Hide Search Matches link\n//\n#searchbox .highlight-link\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0\n margin: 0\n text-align: center\n\n a\n color: var(--color-sidebar-search-icon)\n font-size: var(--font-size--small--2)\n\n////////////////////////////////////////////////////////////////////////////////\n// Structure/Skeleton of the navigation tree (left)\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-tree\n font-size: var(--sidebar-item-font-size)\n margin-top: var(--sidebar-tree-space-above)\n margin-bottom: var(--sidebar-item-spacing-vertical)\n\n ul\n padding: 0\n margin-top: 0\n margin-bottom: 0\n\n display: flex\n flex-direction: column\n\n list-style: none\n\n li\n position: relative\n margin: 0\n\n > ul\n margin-left: var(--sidebar-item-spacing-horizontal)\n\n .icon\n color: var(--color-sidebar-link-text)\n\n .reference\n box-sizing: border-box\n color: var(--color-sidebar-link-text)\n\n // Fill the parent.\n display: inline-block\n line-height: var(--sidebar-item-line-height)\n text-decoration: none\n\n // Don't allow long words to cause wrapping.\n overflow-wrap: anywhere\n\n height: 100%\n width: 100%\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n &:hover\n background: var(--color-sidebar-item-background--hover)\n\n // Add a nice little \"external-link\" arrow here.\n &.external::after\n content: url('data:image/svg+xml,')\n margin: 0 0.25rem\n vertical-align: middle\n color: var(--color-sidebar-link-text)\n\n // Make the current page reference bold.\n .current-page > .reference\n font-weight: bold\n\n label\n position: absolute\n top: 0\n right: 0\n height: var(--sidebar-item-height)\n width: var(--sidebar-expander-width)\n\n cursor: pointer\n user-select: none\n\n display: flex\n justify-content: center\n align-items: center\n\n .caption, :not(.caption) > .caption-text\n font-size: var(--sidebar-caption-font-size)\n color: var(--color-sidebar-caption-text)\n\n font-weight: bold\n text-transform: uppercase\n\n margin: var(--sidebar-caption-space-above) 0 0 0\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n // If it has children, add a bit more padding to wrap the content to avoid\n // overlapping with the