├── assets
├── 6vsb.bcif
└── ipymolstar_screenshot.png
├── src
└── ipymolstar
│ ├── panel
│ ├── __init__.py
│ └── pdbemolstar.py
│ ├── __init__.py
│ ├── molviewspec.py
│ ├── static
│ ├── pdbemolstar.js
│ └── pdbe-dark.css
│ └── pdbemolstar.py
├── .gitignore
├── package.json
├── pyproject.toml
├── examples
├── pdbemolstar
│ ├── pyshiny_example.py
│ ├── rainbow_residues.py
│ ├── click_events.py
│ ├── mouseover_highlight.py
│ ├── panel_app.py
│ ├── solara_app.py
│ └── pyhdx_secb_deltaG.csv
├── mwe_solara.py
└── molviewspec
│ ├── solara_cameracontrol.py
│ └── double_hooke.py
├── .github
└── workflows
│ ├── pypi_main.yml
│ ├── pypi_test.yml
│ └── build.yml
├── LICENSE
├── tests
├── test_unit.py
└── _test_notebook.ipynb
├── js
└── molviewspec.js
└── README.md
/assets/6vsb.bcif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/molstar/ipymolstar/HEAD/assets/6vsb.bcif
--------------------------------------------------------------------------------
/assets/ipymolstar_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/molstar/ipymolstar/HEAD/assets/ipymolstar_screenshot.png
--------------------------------------------------------------------------------
/src/ipymolstar/panel/__init__.py:
--------------------------------------------------------------------------------
1 | from ipymolstar.panel.pdbemolstar import PDBeMolstar
2 |
3 | __all__ = ["PDBeMolstar"]
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .venv
3 | dist
4 |
5 | # Python
6 | __pycache__
7 | .ipynb_checkpoints
8 |
9 | dev/
10 | _pdb/
11 | src/ipymolstar/static/molviewspec.js
12 | src/ipymolstar/static/molviewspec.css
--------------------------------------------------------------------------------
/src/ipymolstar/__init__.py:
--------------------------------------------------------------------------------
1 | from ipymolstar.pdbemolstar import PDBeMolstar
2 | from ipymolstar.molviewspec import MolViewSpec
3 |
4 | __version__ = "0.1.0"
5 | __all__ = ["MolViewSpec", "PDBeMolstar", "__version__"]
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "npm run build -- --sourcemap=inline --watch",
4 | "build": "esbuild js/molviewspec.js --minify --format=esm --bundle --outdir=src/ipymolstar/static"
5 | },
6 | "dependencies": {
7 | "molstar": "^4.12.1"
8 | },
9 | "devDependencies": {
10 | "esbuild": "^0.25.1"
11 | }
12 | }
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["hatchling"]
3 | build-backend = "hatchling.build"
4 |
5 | [project]
6 | name = "ipymolstar"
7 | license = { file = "LICENSE" }
8 | dependencies = ["anywidget"]
9 | readme = "README.md"
10 | description = "PDBeMolstar as anywidget"
11 | keywords = ["molstar", "anywidget"]
12 | dynamic = ['version']
13 |
14 | [tool.hatch.version]
15 | path = "src/ipymolstar/__init__.py"
16 |
17 | [tool.hatch.build]
18 | only-packages = true
19 | artifacts = ["src/ipymolstar/static/*"]
20 | exclude = ["**/.venv*"]
21 |
--------------------------------------------------------------------------------
/examples/pdbemolstar/pyshiny_example.py:
--------------------------------------------------------------------------------
1 | from ipymolstar import PDBeMolstar
2 | from shiny import reactive
3 | from shiny.express import input, render, ui
4 | from shinywidgets import reactive_read, render_widget
5 |
6 | ui.input_text("molecule_id", "Molecule id", "1qyn")
7 |
8 |
9 | @render_widget
10 | def molstar():
11 | view = PDBeMolstar(molecule_id="1qyn")
12 | return view
13 |
14 |
15 | @reactive.effect
16 | def _():
17 | molecule_id = input.molecule_id()
18 | # check for valid pdb id
19 | if len(molecule_id) == 4:
20 | molstar.widget.molecule_id = input.molecule_id()
21 |
22 |
23 | @render.text
24 | def center():
25 | event = reactive_read(molstar.widget, "mouseover_event")
26 | return f"Mouseover event: {event}"
27 |
--------------------------------------------------------------------------------
/.github/workflows/pypi_main.yml:
--------------------------------------------------------------------------------
1 | name: PyPi distribute main release
2 | on: [release]
3 |
4 | jobs:
5 | build-n-publish:
6 | name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI
7 | runs-on: ubuntu-latest
8 | permissions:
9 | id-token: write
10 |
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 | with:
15 | fetch-depth: 0
16 | - name: Set up Python 3.10
17 | uses: actions/setup-python@v4
18 | with:
19 | python-version: "3.10"
20 | - name: Install Hatch
21 | run: pip install hatch
22 | - name: Build
23 | run: |
24 | npm install
25 | npm run build
26 | hatch build
27 | - name: Publish distribution 📦 to PyPI
28 | uses: pypa/gh-action-pypi-publish@release/v1
29 |
--------------------------------------------------------------------------------
/.github/workflows/pypi_test.yml:
--------------------------------------------------------------------------------
1 | name: PyPi distribute test push
2 | on: [push]
3 |
4 | jobs:
5 | build-n-publish:
6 | name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI
7 | runs-on: ubuntu-latest
8 | permissions:
9 | id-token: write
10 |
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 | with:
15 | fetch-depth: 0 # only needed for hatch vcs versioning
16 | - name: Set up Python 3.10
17 | uses: actions/setup-python@v4
18 | with:
19 | python-version: "3.10"
20 | - name: Install Hatch
21 | run: pip install hatch
22 | - name: Build
23 | run: |
24 | npm install
25 | npm run build
26 | hatch build
27 | - name: Publish distribution 📦 to Test PyPI
28 | uses: pypa/gh-action-pypi-publish@release/v1
29 | with:
30 | repository-url: https://test.pypi.org/legacy/
31 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Test App Packaging
2 | # https://github.com/astral-sh/uv/issues/1386
3 | on: [push]
4 |
5 | jobs:
6 | test-packaging:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v3
10 |
11 | - name: Set up Python
12 | uses: actions/setup-python@v4
13 | with:
14 | python-version: '3.10'
15 |
16 | - name: Install hatch
17 | run: pip install hatch
18 |
19 | - name: Build project
20 | run: |
21 | npm install
22 | npm run build
23 | hatch build
24 | - name: check the tarball
25 | run: |
26 | tar -tzf dist/*.tar.gz
27 | - name: Install uv
28 | run: pip install uv
29 |
30 | - name: Create and activate venv, install package
31 | run: |
32 | uv venv
33 | source .venv/bin/activate
34 | echo PATH=$PATH >> $GITHUB_ENV
35 | uv pip install pytest molviewspec
36 | uv pip install dist/*.tar.gz
37 | python -m pytest tests
38 |
39 |
--------------------------------------------------------------------------------
/src/ipymolstar/molviewspec.py:
--------------------------------------------------------------------------------
1 | import anywidget
2 | import pathlib
3 | import traitlets
4 |
5 | # https://github.com/molstar/molstar/blob/80415a2771fcddbca3cc13ddba4be10c92a1454b/src/apps/viewer/app.ts#L88
6 | DEFAULT_VIEWER_OPTIONS = {
7 | "layoutIsExpanded": False,
8 | "layoutShowControls": False,
9 | }
10 |
11 | # https://github.com/molstar/molstar/blob/80415a2771fcddbca3cc13ddba4be10c92a1454b/src/extensions/mvs/load.ts#L37
12 | DEFAULT_MVS_LOAD_OPTIONS = {
13 | "replaceExisting": True,
14 | "sanityChecks": True,
15 | }
16 |
17 |
18 | class MolViewSpec(anywidget.AnyWidget):
19 | _esm = pathlib.Path(__file__).parent / "static" / "molviewspec.js"
20 | _css = pathlib.Path(__file__).parent / "static" / "molviewspec.css"
21 |
22 | width = traitlets.Unicode("100%").tag(sync=True)
23 | height = traitlets.Unicode("500px").tag(sync=True)
24 | msvj_data = traitlets.Unicode("").tag(sync=True)
25 |
26 | viewer_options = traitlets.Dict(DEFAULT_VIEWER_OPTIONS).tag(sync=True)
27 | mvs_load_options = traitlets.Dict(DEFAULT_MVS_LOAD_OPTIONS).tag(sync=True)
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Jochem Smit
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/examples/mwe_solara.py:
--------------------------------------------------------------------------------
1 | import solara
2 | from ipymolstar import PDBeMolstar, MolViewSpec
3 | from molviewspec import create_builder
4 |
5 |
6 | @solara.component
7 | def Page():
8 | solara.Title("Solara + PDBeMolstar + MolViewSpec")
9 | molecule_id = solara.use_reactive("1qyn")
10 |
11 | builder = create_builder()
12 | (
13 | builder.download(url=f"https://files.rcsb.org/download/{molecule_id.value}.pdb")
14 | .parse(format="pdb")
15 | .model_structure()
16 | .component()
17 | .representation()
18 | .color(color="blue")
19 | )
20 |
21 | with solara.Sidebar():
22 | solara.InputText(label="Molecule ID", value=molecule_id)
23 |
24 | with solara.Columns():
25 | with solara.Card("PDBeMolstar"):
26 | PDBeMolstar.element(
27 | hide_controls_icon=True,
28 | hide_expand_icon=True,
29 | hide_settings_icon=True,
30 | hide_selection_icon=True,
31 | molecule_id=molecule_id.value,
32 | )
33 | with solara.Card("MolViewSpec"):
34 | MolViewSpec.element(msvj_data=builder.get_state().dumps())
35 |
--------------------------------------------------------------------------------
/tests/test_unit.py:
--------------------------------------------------------------------------------
1 | from ipymolstar import PDBeMolstar, MolViewSpec
2 | from molviewspec import create_builder
3 |
4 |
5 | def test_pdbemolstar():
6 | """Test the PDBeMolstar component"""
7 | # Create a PDBeMolstar instance
8 | pdbe_molstar = PDBeMolstar(
9 | molecule_id="1qyn",
10 | hide_controls_icon=True,
11 | hide_expand_icon=True,
12 | hide_settings_icon=True,
13 | hide_selection_icon=True,
14 | )
15 | # Check if the instance is created successfully
16 | assert isinstance(pdbe_molstar, PDBeMolstar), (
17 | "Failed to create PDBeMolstar instance"
18 | )
19 |
20 |
21 | def test_molviewspec():
22 | """Test the MolViewSpec component"""
23 | # Create a MolViewSpec instance
24 | builder = create_builder()
25 | (
26 | builder.download(url=f"https://files.rcsb.org/download/1qyn.pdb")
27 | .parse(format="pdb")
28 | .model_structure()
29 | .component()
30 | .representation()
31 | .color(color="blue")
32 | )
33 |
34 | molview_spec = MolViewSpec(
35 | msvj_data=builder.get_state().dumps(),
36 | )
37 | # Check if the instance is created successfully
38 | assert isinstance(molview_spec, MolViewSpec), (
39 | "Failed to create MolViewSpec instance"
40 | )
41 |
--------------------------------------------------------------------------------
/js/molviewspec.js:
--------------------------------------------------------------------------------
1 | // import "./widget.css";
2 | import 'molstar/build/viewer/molstar.css';
3 | import { Viewer, PluginExtensions } from 'molstar/build/viewer/molstar'
4 |
5 |
6 | function updateFromMSVJ(viewer, msvj, options) {
7 | const mvsData = PluginExtensions.mvs.MVSData.fromMVSJ(msvj);
8 | PluginExtensions.mvs.loadMVS(viewer.plugin, mvsData, options);
9 | }
10 |
11 | function render({ model, el }) {
12 | const uniqueId = `viewer_container_${Math.random().toString(36).slice(2, 11)}`;
13 | let viewerContainer = document.createElement("div");
14 | viewerContainer.id = uniqueId;
15 |
16 | viewerContainer.style.height = model.get("height");
17 | viewerContainer.style.width = model.get("width");
18 |
19 | // Make the container responsive
20 | viewerContainer.style.maxWidth = "100%";
21 | viewerContainer.style.boxSizing = "border-box";
22 |
23 | let viewer = null;
24 |
25 | const ViewerOptions = model.get("viewer_options");
26 |
27 | // Initialize the viewer first
28 | Viewer.create(viewerContainer, ViewerOptions).then(v => {
29 | viewer = v;
30 | // If we have an initial schema, load it
31 | const msvj = model.get("msvj_data");
32 | if (msvj && msvj.trim() !== "") {
33 | const options = model.get('mvs_load_options')
34 | updateFromMSVJ(viewer, msvj, options)
35 | };
36 |
37 | });
38 |
39 | el.appendChild(viewerContainer);
40 |
41 | model.on("change:msvj_data", () => {
42 | console.log("Schema changed");
43 | const msvj = model.get("msvj_data");
44 | if (msvj && msvj.trim() !== "") {
45 | const options = model.get('mvs_load_options')
46 | updateFromMSVJ(viewer, msvj, options)
47 | };
48 | });
49 |
50 | // Watch for changes to dimensions
51 | model.on("change:height", () => {
52 | viewerContainer.style.height = model.get("height");
53 | });
54 |
55 | model.on("change:width", () => {
56 | viewerContainer.style.width = model.get("width");
57 | });
58 | }
59 |
60 | export default { render };
61 |
--------------------------------------------------------------------------------
/examples/pdbemolstar/rainbow_residues.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import solara
4 | import solara.lab
5 | from Bio.PDB import PDBParser, Structure
6 | from ipymolstar import PDBeMolstar
7 | from matplotlib import colormaps
8 | from matplotlib.colors import Normalize
9 |
10 | AMINO_ACIDS = [
11 | "ALA",
12 | "ARG",
13 | "ASN",
14 | "ASP",
15 | "CYS",
16 | "GLN",
17 | "GLU",
18 | "GLY",
19 | "HIS",
20 | "ILE",
21 | "LEU",
22 | "LYS",
23 | "MET",
24 | "PHE",
25 | "PRO",
26 | "PYL",
27 | "SEC",
28 | "SER",
29 | "THR",
30 | "TRP",
31 | "TYR",
32 | "VAL",
33 | ]
34 |
35 | # %%
36 |
37 | root = Path(__file__).parent.parent
38 | pdb_path = root / "assets" / "1qyn.pdb"
39 |
40 | parser = PDBParser(QUIET=True)
41 | structure = parser.get_structure("1qyn", pdb_path)
42 | MAX_R = max(r.id[1] for r in structure.get_residues())
43 |
44 | # %%
45 |
46 | custom_data = {"data": pdb_path.read_bytes(), "format": "pdb", "binary": False}
47 |
48 |
49 | # %%
50 | def color_residues(
51 | structure: Structure.Structure, auth: bool = False, phase: int = 0
52 | ) -> dict:
53 | _, resn, _ = zip(
54 | *[r.id for r in structure.get_residues() if r.get_resname() in AMINO_ACIDS]
55 | )
56 |
57 | rmin, rmax = min(resn), max(resn)
58 | # todo check for off by one errors
59 | norm = Normalize(vmin=rmin, vmax=rmax)
60 | auth_str = "_auth" if auth else ""
61 |
62 | cmap = colormaps["hsv"]
63 | data = []
64 | for i in range(rmin, rmax + 1):
65 | range_size = rmax + 1 - rmin
66 | j = rmin + ((i - rmin + phase) % range_size)
67 | r, g, b, a = cmap(norm(i), bytes=True)
68 | color = {"r": int(r), "g": int(g), "b": int(b)}
69 | elem = {
70 | f"start{auth_str}_residue_number": j,
71 | f"end{auth_str}_residue_number": j,
72 | "color": color,
73 | "focus": False,
74 | }
75 | data.append(elem)
76 |
77 | color_data = {"data": data, "nonSelectedColor": None}
78 | return color_data
79 |
80 |
81 | @solara.component
82 | def Page():
83 | phase = solara.use_reactive(0.0)
84 | color_data = color_residues(structure, auth=True, phase=phase.value)
85 | with solara.Card():
86 | PDBeMolstar.element(
87 | custom_data=custom_data, hide_water=True, color_data=color_data
88 | )
89 | solara.FloatSlider(label="Phase", min=0, max=MAX_R, value=phase, step=1)
90 |
91 |
92 | Page()
93 |
--------------------------------------------------------------------------------
/examples/pdbemolstar/click_events.py:
--------------------------------------------------------------------------------
1 | """
2 | This example shows to how use click interactions
3 | View on pycafé: https://app.py.cafe/jhsmit/ipymolstar-click-interaction
4 | """
5 |
6 | from string import Template
7 |
8 | import solara
9 | from ipymolstar import PDBeMolstar
10 |
11 | url_template = Template(
12 | "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/CID/$cid/record/SDF?record_type=3d&response_type=save&response_basename=Conformer3D_COMPOUND_CID_$cid"
13 | )
14 |
15 |
16 | AA_LUT = {
17 | "ARG": 6322,
18 | "HIS": 6274,
19 | "LYS": 5962,
20 | "ASP": 5960,
21 | "GLU": 33032,
22 | "SER": 5951,
23 | "THR": 6288,
24 | "ASN": 6267,
25 | "GLN": 5961,
26 | "CYS": 5862,
27 | "SEC": 6326983,
28 | "GLY": 750,
29 | "PRO": 145742,
30 | "ALA": 5950,
31 | "VAL": 6287,
32 | "ILE": 6306,
33 | "LEU": 6106,
34 | "MET": 6137,
35 | "PHE": 6140,
36 | "TYR": 6057,
37 | "TRP": 6305,
38 | }
39 |
40 | custom_data_initial = dict(
41 | url=url_template.substitute(cid=AA_LUT["TRP"]),
42 | format="sdf",
43 | binary=False,
44 | )
45 |
46 |
47 | molecule_id = "1QYN"
48 |
49 | # %%
50 | s = """empty
51 | -ISIS-
52 |
53 | 0 0 0 0 0 0 0 0 0 0999 V2000
54 | M END
55 | $$$$
56 | """
57 |
58 | b = s.encode()
59 | empty_data = {
60 | "data": b,
61 | "format": "sdf",
62 | "binary": True,
63 | }
64 |
65 | # %%
66 |
67 |
68 | @solara.component
69 | def Page():
70 | solara.Title("ipymolstar - Click Events")
71 | click_event = solara.use_reactive(None)
72 | custom_data = solara.use_reactive(empty_data)
73 | molecule_id = solara.use_reactive("1QYN")
74 | amino_acid = solara.use_reactive("")
75 |
76 | def on_click(event):
77 | click_event.set(event)
78 | aa_tla = event["comp_id"]
79 |
80 | if aa_tla in AA_LUT:
81 | amino_acid.set(aa_tla)
82 | custom_data.set(
83 | dict(
84 | url=url_template.substitute(cid=AA_LUT[aa_tla]),
85 | format="sdf",
86 | binary=False,
87 | )
88 | )
89 | else:
90 | amino_acid.set("")
91 | custom_data.set(empty_data)
92 |
93 | with solara.Sidebar():
94 | solara.InputText(
95 | label="Molecule ID", value=molecule_id, on_value=molecule_id.set
96 | )
97 |
98 | with solara.Columns():
99 | with solara.Card(f"Protein: {molecule_id.value}"):
100 | PDBeMolstar.element( # type: ignore
101 | molecule_id=molecule_id.value.lower(),
102 | hide_controls_icon=True,
103 | hide_expand_icon=True,
104 | hide_settings_icon=True,
105 | hide_selection_icon=False,
106 | select_interaction=False, # dont show local interactions on click
107 | click_focus=False, # dont zoom on click
108 | on_click_event=on_click,
109 | )
110 |
111 | with solara.Card(f"Amino Acid: {amino_acid.value}"):
112 | PDBeMolstar.element( # type: ignore
113 | molecule_id="",
114 | custom_data=custom_data.value,
115 | hide_controls_icon=True,
116 | hide_expand_icon=True,
117 | hide_settings_icon=True,
118 | hide_selection_icon=False,
119 | click_focus=False,
120 | )
121 |
--------------------------------------------------------------------------------
/tests/_test_notebook.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Manual tests of PDBeMolstar\n",
8 | "\n",
9 | "Run cells and confirm is described effect is observed"
10 | ]
11 | },
12 | {
13 | "cell_type": "code",
14 | "execution_count": null,
15 | "metadata": {},
16 | "outputs": [],
17 | "source": [
18 | "from ipymolstar import PDBeMolstar\n",
19 | "from ipywidgets import VBox\n"
20 | ]
21 | },
22 | {
23 | "cell_type": "markdown",
24 | "metadata": {},
25 | "source": [
26 | "## height\n",
27 | "expected: vbox with two viewers with 200/400 px height"
28 | ]
29 | },
30 | {
31 | "cell_type": "code",
32 | "execution_count": null,
33 | "metadata": {},
34 | "outputs": [],
35 | "source": [
36 | "\n",
37 | "v1 = PDBeMolstar(molecule_id='1qyn', height=\"200px\")\n",
38 | "v2 = PDBeMolstar(molecule_id='1qyn', height=\"400px\")\n",
39 | "VBox([v1, v2])\n"
40 | ]
41 | },
42 | {
43 | "cell_type": "code",
44 | "execution_count": null,
45 | "metadata": {},
46 | "outputs": [],
47 | "source": [
48 | "#using widgets layout\n",
49 | "PDBeMolstar(molecule_id='1qyn',layout=Layout(width='500px', height='250px')) "
50 | ]
51 | },
52 | {
53 | "cell_type": "markdown",
54 | "metadata": {},
55 | "source": [
56 | "## molecule_id\n",
57 | "expected: Initial load of '1qyn', rerender to '2nnu' on trigger"
58 | ]
59 | },
60 | {
61 | "cell_type": "code",
62 | "execution_count": null,
63 | "metadata": {},
64 | "outputs": [],
65 | "source": [
66 | "v = PDBeMolstar(molecule_id='1qyn', height='150px')\n",
67 | "v"
68 | ]
69 | },
70 | {
71 | "cell_type": "code",
72 | "execution_count": null,
73 | "metadata": {},
74 | "outputs": [],
75 | "source": [
76 | "v.molecule_id = '2nnu'"
77 | ]
78 | },
79 | {
80 | "cell_type": "markdown",
81 | "metadata": {},
82 | "source": [
83 | "# custom_data\n",
84 | "expected: Initial load of `1cbs`, rerender to chainA of `1qyn` on trigger (trigger does not work)"
85 | ]
86 | },
87 | {
88 | "cell_type": "code",
89 | "execution_count": null,
90 | "metadata": {},
91 | "outputs": [],
92 | "source": [
93 | "custom_data = dict(url='https://www.ebi.ac.uk/pdbe/model-server/v1/1cbs/atoms?label_entity_id=1&auth_asym_id=A&encoding=bcif', format='cif', binary=True)\n",
94 | "v = PDBeMolstar(custom_data=custom_data, height='150px', alphafold_view=True)\n",
95 | "v"
96 | ]
97 | },
98 | {
99 | "cell_type": "code",
100 | "execution_count": null,
101 | "metadata": {},
102 | "outputs": [],
103 | "source": [
104 | "new_custom_data = dict(url='https://www.ebi.ac.uk/pdbe/model-server/v1/1qyn/atoms?label_entity_id=1&auth_asym_id=A&encoding=bcif', format='cif', binary=True)\n",
105 | "v.custom_data = new_custom_data"
106 | ]
107 | },
108 | {
109 | "cell_type": "code",
110 | "execution_count": null,
111 | "metadata": {},
112 | "outputs": [],
113 | "source": [
114 | "\n",
115 | "#alphafold colors and tooltips\n",
116 | "custom_data = dict(url='https://alphafold.ebi.ac.uk/files/AF-Q8I3H7-F1-model_v4.cif', format='cif', binary=False)\n",
117 | "v = PDBeMolstar(custom_data=custom_data, height='150px', alphafold_view=True)\n",
118 | "\n",
119 | "v"
120 | ]
121 | },
122 | {
123 | "cell_type": "code",
124 | "execution_count": null,
125 | "metadata": {},
126 | "outputs": [],
127 | "source": [
128 | "from pathlib import Path \n",
129 | "fpth = Path().resolve().parent / 'assets' / '6vsb.bcif'\n",
130 | "custom_data = {\n",
131 | " 'data': fpth.read_bytes(),\n",
132 | " 'format': 'cif',\n",
133 | " 'binary': True,\n",
134 | " }\n",
135 | "view = PDBeMolstar(\n",
136 | " custom_data=custom_data, \n",
137 | " hide_controls_icon=True, \n",
138 | " hide_expand_icon=True, \n",
139 | " hide_settings_icon=True, \n",
140 | " hide_selection_icon=True, \n",
141 | " hide_animation_icon=True,\n",
142 | " hide_water=True,\n",
143 | " hide_carbs=True,\n",
144 | ")\n",
145 | "view"
146 | ]
147 | }
148 | ],
149 | "metadata": {
150 | "kernelspec": {
151 | "display_name": ".venv",
152 | "language": "python",
153 | "name": "python3"
154 | },
155 | "language_info": {
156 | "codemirror_mode": {
157 | "name": "ipython",
158 | "version": 3
159 | },
160 | "file_extension": ".py",
161 | "mimetype": "text/x-python",
162 | "name": "python",
163 | "nbconvert_exporter": "python",
164 | "pygments_lexer": "ipython3",
165 | "version": "3.10.13"
166 | }
167 | },
168 | "nbformat": 4,
169 | "nbformat_minor": 4
170 | }
171 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ipymolstar
2 |
3 | 
4 |
5 |
6 | ## Live Demos
7 |
8 | Give `ipymolstar` a spin without even installing python!
9 |
10 |
11 | - Try it in jupyter lab via [JupyterLite](https://github.com/Jhsmit/ipymolstar-demo) 🌍🚀
12 | - Explore the solara ☀️ demo application on [HuggingFace](https://huggingface.co/spaces/Jhsmit/ipymolstar-demo) 🤗
13 | - Grab a cup and play with the [solara](https://app.py.cafe/jhsmit/ipymolstar-solara), [pyshiny](https://py.cafe/jhsmit/ipymolstar-shiny) or [panel](https://app.py.cafe/jhsmit/ipymolstar-panel) live demo's on PyCafé ☕
14 | - Upload your Alphafold3 .zip result and view plddt or chain colors in the `solarafold` result viewer on [huggingface](https://huggingface.co/spaces/Jhsmit/solarafold) 🤗
15 | - Control the camera 📷 with [MolViewSpec](https://pypi.org/project/molviewspec/) from python 🐍 in a solara ☀️ dashboard on [PyCafé](https://py.cafe/jhsmit/molviewspec-protein-visualization) ☕
16 | - Use [MolViewSpec](https://pypi.org/project/molviewspec/) to define primitives 🟦🟢 and animations 🎞️, check out the Double Hooke [3DPGA dynamics](https://enki.ws/ganja.js/examples/pga_dyn.html) example on [PyCafé](https://py.cafe/jhsmit/ipymolstar-kingdon-double-hooke) ☕, adapted from the [kingdon](https://github.com/tBuLi/kingdon) teahouse 🍵;
17 |
18 | You can find other examples in the examples directory.
19 |
20 | ## Preview
21 |
22 | You can run a quick preview of `ipymolstar` in a notebook with [juv](https://github.com/manzt/juv):
23 |
24 | ```sh
25 | juv run example.ipynb
26 | ```
27 |
28 | ## Installation
29 |
30 | ```sh
31 | pip install ipymolstar
32 | ```
33 | > [!WARNING]
34 | > Make sure you install ipymolstar in an environment that contains your installation of Jupyter. If you have installed Jupyter in a different environment from your project (requiring you to use a named, non-default kernel), you will have to install ipymolstar (or only anywidget) in your Jupyter environment as well.
35 |
36 |
37 | ## Use
38 |
39 | ```python
40 | from ipymolstar import PDBeMolstar
41 | view = PDBeMolstar(molecule_id='1qyn', theme='light', hide_water=True)
42 | view
43 | ```
44 |
45 | Loading local data, hiding the buttons:
46 |
47 | ```python
48 | from pathlib import Path
49 | fpth = Path().resolve() / 'assets' / '6vsb.bcif'
50 | custom_data = {
51 | 'data': fpth.read_bytes(),
52 | 'format': 'cif',
53 | 'binary': True,
54 | }
55 | view = PDBeMolstar(
56 | custom_data=custom_data,
57 | hide_controls_icon=True,
58 | hide_expand_icon=True,
59 | hide_settings_icon=True,
60 | hide_selection_icon=True,
61 | hide_animation_icon=True,
62 | hide_water=True,
63 | hide_carbs=True,
64 | )
65 | view
66 | ```
67 |
68 | See the example notebook for more advanced usage.
69 | Solara example code can be found [here](https://github.com/Jhsmit/ploomber-solara-ipymolstar)
70 |
71 | ## Citing
72 |
73 | `ipymolstar` uses [anywidget](https://github.com/manzt/anywidget) to create a widgets based on [Mol*](https://molstar.org/)
74 |
75 | To cite `anywidget`:
76 | > Trevor Manz, Nezar Abdennur, Nils Gehlenborg: anywidget: reusable widgets for interactive analysis and visualization in computational notebooks. Journal of Open Source Software, 2024; [10.21105/joss.06939](https://doi.org/10.21105/joss.06939)
77 |
78 | To cite Mol*:
79 | > David Sehnal, Sebastian Bittrich, Mandar Deshpande, Radka Svobodová, Karel Berka, Václav Bazgier, Sameer Velankar, Stephen K Burley, Jaroslav Koča, Alexander S Rose: Mol* Viewer: modern web app for 3D visualization and analysis of large biomolecular structures, Nucleic Acids Research, 2021; [10.1093/nar/gkab31](https://doi.org/10.1093/nar/gkab314).
80 |
81 |
82 | ### PDBeMolstar
83 | The PDBeMolstar widget is based on the [PDBe integration](https://github.com/molstar/pdbe-molstar) of Mol*.
84 |
85 |
86 | ### MolViewSpec
87 |
88 | The MolViewSpec widget is based on [MolViewSpec](https://github.com/molstar/mol-view-spec). To cite MolViewSpec:
89 |
90 | > Sebastian Bittrich, Adam Midlik, Mihaly Varadi, Sameer Velankar, Stephen K. Burley, Jasmine Y. Young, David Sehnal, Brinda Vallat: Describing and Sharing Molecular Visualizations Using the MolViewSpec Toolkit, Current Protocols, 2024; [10.1002/cpz1.1099](https://doi.org/10.1002/cpz1.1099)
91 |
92 | See also the [RCSB citation policies](https://www.rcsb.org/pages/policies) for additional citation information.
93 |
94 | ## Development
95 |
96 |
97 | The molviewspec widget front-end code bundles it's JavaScript dependencies. After setting up Python,
98 | make sure to install these dependencies locally:
99 |
100 | ```sh
101 | npm install
102 | ```
103 |
104 | While developing, you can run the following in a separate terminal to automatically
105 | rebuild JavaScript as you make changes:
106 |
107 | ```sh
108 | npm run dev
109 | ```
110 |
111 |
112 | ### Creating a new release
113 |
114 | - update `__version__` in `__init__.py`
115 | - create a new release on GitHub, choose as tag 'v' + `__version__`; ie 'v0.0.3'
116 | - GitHub actions should automatically deploy to PyPi
117 |
118 | ### Hot reloading
119 |
120 | To enable anywidget hot reloading, you need to set the env var `ANYWIDGET_HMR` to 1.
121 |
122 | Windows:
123 | ```bash
124 | set ANYWIDGET_HMR=1
125 | jupyter lab
126 | ```
127 |
--------------------------------------------------------------------------------
/examples/molviewspec/solara_cameracontrol.py:
--------------------------------------------------------------------------------
1 | import copy
2 | import math
3 | import warnings
4 | from io import StringIO
5 |
6 | import numpy as np
7 | import requests
8 | import solara
9 | import solara.lab
10 | from Bio.PDB import PDBParser
11 | from Bio.PDB.PDBExceptions import PDBConstructionWarning
12 | from molviewspec import GlobalMetadata, create_builder
13 |
14 | from ipymolstar.molviewspec import MolViewSpec
15 |
16 | # Suppress only PDBConstructionWarnings
17 | warnings.simplefilter("ignore", PDBConstructionWarning)
18 |
19 |
20 | # https://github.com/molstar/mol-view-spec/blob/def8ad6cdc351dbe01e29bf717e58e004bd10408/molviewspec/app/api/examples.py#L1819
21 | def target_spherical_to_tpu(
22 | target: tuple[float, float, float],
23 | phi: float = 0,
24 | theta: float = 0,
25 | radius: float = 100,
26 | ):
27 | x, y, z = target
28 | phi, theta = math.radians(phi), math.radians(theta)
29 | direction = (
30 | -math.sin(phi) * math.cos(theta),
31 | -math.sin(theta),
32 | -math.cos(phi) * math.cos(theta),
33 | )
34 | position = (
35 | x - direction[0] * radius,
36 | y - direction[1] * radius,
37 | z - direction[2] * radius,
38 | )
39 | up = (0, 1, 0)
40 | return target, position, up
41 |
42 |
43 | def fetch_pdb(pdb_id) -> StringIO:
44 | url = f"https://files.rcsb.org/download/{pdb_id}.pdb"
45 | response = requests.get(url)
46 | if response.status_code == 200:
47 | sio = StringIO(response.text)
48 | sio.seek(0)
49 | return sio
50 | else:
51 | raise requests.HTTPError(f"Failed to download PDB file {pdb_id}")
52 |
53 |
54 | def load_structure(pdb_id: str):
55 | parser = PDBParser()
56 | structure = parser.get_structure(pdb_id, fetch_pdb(pdb_id))
57 |
58 | return structure
59 |
60 |
61 | # %%
62 | def calculate_com(structure) -> tuple[float, float, float]:
63 | # Collect C-alpha atom coordinates
64 | ca_coords = []
65 |
66 | for model in structure:
67 | for chain in model:
68 | for residue in chain:
69 | if "CA" in residue: # Check for C-alpha atom
70 | ca_coords.append(residue["CA"].coord)
71 |
72 | # Compute average (center of mass)
73 | ca_coords = np.array(ca_coords)
74 | return tuple(float(f) for f in np.mean(ca_coords, axis=0))
75 |
76 |
77 | metadata = GlobalMetadata(
78 | title=None,
79 | description=None,
80 | description_format=None,
81 | )
82 |
83 |
84 | @solara.component
85 | def Page():
86 | pdb_id = solara.use_reactive("1qyn")
87 | radius = solara.use_reactive(100.0)
88 | phi = solara.use_reactive(0.0)
89 | theta = solara.use_reactive(0.0)
90 |
91 | with solara.AppBar():
92 | solara.AppBarTitle("MolViewSpec + Solara: Camera control from python")
93 |
94 | def load_structure_and_com():
95 | builder = create_builder()
96 | (
97 | builder.download(url=f"https://files.rcsb.org/download/{pdb_id.value}.pdb")
98 | .parse(format="pdb")
99 | .model_structure()
100 | .component()
101 | .representation()
102 | .color(color="blue")
103 | )
104 |
105 | structure = load_structure(pdb_id.value)
106 | com = calculate_com(structure)
107 |
108 | return builder, com
109 |
110 | load_task = solara.lab.use_task(load_structure_and_com, dependencies=[pdb_id.value])
111 |
112 | def rot_phi(value):
113 | new_value = (phi.value + value) % 360
114 | phi.set(new_value)
115 |
116 | def rot_theta(value):
117 | new_value = (theta.value + value) % 360
118 | theta.set(new_value)
119 |
120 | with solara.ColumnsResponsive([4, 8]):
121 | with solara.Card("Controls"):
122 | solara.InputText(label="PDB ID", value=pdb_id)
123 | solara.SliderFloat(label="Radius", value=radius, min=50, max=200, step=0.5)
124 | solara.SliderFloat(label="Phi", value=phi, min=0, max=360, step=1)
125 | solara.SliderFloat(label="Theta", value=theta, min=0, max=360, step=1)
126 | with solara.Row(justify="space-between"):
127 | solara.Button(label="-90° y", on_click=lambda: rot_phi(-90))
128 | solara.Button(label="+90° y", on_click=lambda: rot_phi(90))
129 | solara.Button(label="-90° x", on_click=lambda: rot_theta(-90))
130 | solara.Button(label="+90° x", on_click=lambda: rot_theta(90))
131 |
132 | with solara.Card("Protein view"):
133 | if load_task.latest is None:
134 | solara.Markdown("Loading...")
135 | else:
136 | builder, com = (
137 | load_task.value if load_task.finished else load_task.latest
138 | )
139 |
140 | target, position, up = target_spherical_to_tpu(
141 | target=com,
142 | phi=phi.value,
143 | theta=theta.value,
144 | radius=radius.value,
145 | )
146 | local_builder = builder.model_copy()
147 | local_builder.camera(target=target, position=position, up=up)
148 |
149 | msvj_data = local_builder.get_state().dumps()
150 | with solara.Div(style="opacity: 0.3" if load_task.pending else None):
151 | view = MolViewSpec.element(msvj_data=msvj_data)
152 |
--------------------------------------------------------------------------------
/examples/pdbemolstar/mouseover_highlight.py:
--------------------------------------------------------------------------------
1 | """
2 | Example solara app with two-way hover/highlight between ipymolstar and altair
3 | View on pycafé: https://app.py.cafe/jhsmit/ipymolstar-altair-hover-highlight
4 | """
5 |
6 | # %%
7 | from pathlib import Path
8 |
9 | import altair as alt
10 | import numpy as np
11 | import pandas as pd
12 | import solara
13 | from cmap import Colormap
14 | from ipymolstar import PDBeMolstar
15 |
16 | # color limits in kJ/mol
17 | VMIN = 10
18 | VMAX = 40
19 | NO_COVERAGE = "#8c8c8c"
20 | HIGHLIGHT_COLOR = "#e933f8"
21 | cmap = Colormap("tol:rainbow_PuRd_r", bad=NO_COVERAGE)
22 | domain = np.linspace(VMIN, VMAX, 256, endpoint=True)
23 | scale = alt.Scale(domain=list(domain), range=cmap.to_altair())
24 |
25 | # %%
26 |
27 |
28 | def norm(x, vmin=VMIN, vmax=VMAX):
29 | return (x - vmin) / (vmax - vmin)
30 |
31 |
32 | # %%
33 | script_path = Path(__file__).parent
34 | kwargs = {"comment": "#", "header": [0]}
35 | data = pd.read_csv(script_path / "pyhdx_secb_deltaG.csv", **kwargs).rename(
36 | columns={"r_number": "residue"}
37 | )
38 | data = data.drop(data.index[-1])[["residue", "deltaG"]]
39 | data["deltaG"] *= 1e-3
40 |
41 |
42 | # %%
43 | cmap = Colormap("tol:rainbow_PuRd_r", bad=NO_COVERAGE)
44 |
45 | rgba_array = cmap(norm(data["deltaG"]), bytes=True)
46 | base_v = np.vectorize(np.base_repr)
47 | ints = rgba_array.astype(np.uint8).view(dtype=np.uint32).byteswap()
48 | padded = np.char.rjust(base_v(ints // 2**8, 16), 6, "0")
49 | hex_colors = np.char.add("#", padded).squeeze()
50 |
51 | # %%
52 | color_data = {
53 | "data": [
54 | {"residue_number": resi, "color": hcolor.lower()}
55 | for resi, hcolor in zip(data["residue"], hex_colors)
56 | ],
57 | "nonSelectedColor": NO_COVERAGE,
58 | }
59 |
60 | # %%
61 |
62 | tooltips = {
63 | "data": [
64 | {
65 | "residue_number": resi,
66 | "tooltip": f"ΔG: {value:.2f} kJ/mol"
67 | if not np.isnan(value)
68 | else "No coverage",
69 | }
70 | for resi, value in zip(data["residue"], data["deltaG"])
71 | ]
72 | }
73 |
74 |
75 | # Create a selection that chooses the nearest point & selects based on x-value
76 | nearest = alt.selection_point(
77 | name="point",
78 | nearest=True,
79 | on="pointerover",
80 | fields=["residue"],
81 | empty=False,
82 | clear="mouseout",
83 | )
84 |
85 | pad = (VMAX - VMIN) * 0.05
86 |
87 | # The basic scatter
88 | scatter = (
89 | alt.Chart(data)
90 | .mark_circle(interpolate="basis", size=200)
91 | .encode(
92 | x=alt.X("residue:Q", title="Residue Number"),
93 | y=alt.Y(
94 | "deltaG:Q",
95 | title="ΔG (kJ/mol)",
96 | scale=alt.Scale(domain=(VMAX + pad, VMIN - pad)),
97 | ),
98 | color=alt.Color("deltaG:Q", scale=scale, title="ΔG (kJ/mol)"),
99 | )
100 | )
101 |
102 | # %%
103 |
104 | # Transparent selectors across the chart. This is what tells us
105 | # the x-value of the cursor
106 | selectors = (
107 | alt.Chart(data)
108 | .mark_point()
109 | .encode(
110 | x="residue:Q",
111 | opacity=alt.value(0),
112 | )
113 | .add_params(nearest)
114 | )
115 |
116 | # Draw a rule at the location of the selection
117 | rule = (
118 | alt.Chart(data)
119 | .mark_rule(color="gray", size=2)
120 | .encode(
121 | x="residue:Q",
122 | )
123 | .transform_filter(nearest)
124 | )
125 |
126 | vline = (
127 | alt.Chart(pd.DataFrame({"x": [0]}))
128 | .mark_rule(color=HIGHLIGHT_COLOR, size=2)
129 | .encode(x="x:Q")
130 | )
131 |
132 |
133 | # Put the five layers into a chart and bind the data
134 | chart = (
135 | alt.layer(scatter, vline, selectors, rule).properties(
136 | width="container",
137 | height=480, # autosize height?
138 | )
139 | # .configure(autosize="fit")
140 | )
141 |
142 | spec = chart.to_dict()
143 | data_name = spec["layer"][1]["data"]["name"]
144 |
145 |
146 | @solara.component
147 | def SelectChart(on_selections, line_value):
148 | spec["datasets"][data_name] = [{"x": line_value}]
149 | view = alt.JupyterChart.element(
150 | chart=chart, spec=spec, embed_options={"actions": False}
151 | )
152 |
153 | def bind():
154 | real = solara.get_widget(view)
155 | real.selections.observe(on_selections, "point")
156 |
157 | solara.use_effect(bind, [])
158 |
159 |
160 | @solara.component
161 | def Page():
162 | # residue number to highlight in altair chart
163 | line_number = solara.use_reactive(None)
164 |
165 | # residue number to highlight in protein view
166 | highlight_number = solara.use_reactive(None)
167 | with solara.AppBar():
168 | solara.AppBarTitle("altair/ipymolstar bidirectional highlight")
169 | solara.Style(
170 | """
171 | .vega-embed {
172 | overflow: visible;
173 | width: 100% !important;
174 | height: 800px !important;
175 | }"""
176 | )
177 |
178 | def on_mouseover(value):
179 | r = value.get("residueNumber", None)
180 | line_number.set(r)
181 |
182 | def on_mouseout(value):
183 | on_mouseover({})
184 |
185 | def on_selections(event):
186 | try:
187 | r = event["new"].value[0]["residue"]
188 | highlight_number.set(r)
189 | except (IndexError, KeyError):
190 | highlight_number.set(None)
191 |
192 | with solara.ColumnsResponsive([4, 8]):
193 | with solara.Card(style={"height": "550px"}):
194 | PDBeMolstar.element(
195 | molecule_id="1qyn",
196 | color_data=color_data,
197 | hide_water=True,
198 | tooltips=tooltips,
199 | height="500px",
200 | highlight={"data": [{"residue_number": int(highlight_number.value)}]}
201 | if highlight_number.value
202 | else None,
203 | highlight_color=HIGHLIGHT_COLOR,
204 | on_mouseover_event=on_mouseover,
205 | on_mouseout_event=on_mouseout,
206 | )
207 | with solara.Card(style={"height": "550px"}):
208 | SelectChart(on_selections, line_number.value)
209 |
210 |
211 | # %%
212 |
--------------------------------------------------------------------------------
/src/ipymolstar/panel/pdbemolstar.py:
--------------------------------------------------------------------------------
1 | import pathlib
2 | from typing import Optional
3 |
4 | from ipymolstar.pdbemolstar import THEMES, Color, QueryParam, ResetParam
5 |
6 | try:
7 | import panel as pn
8 | except ImportError:
9 | msg = "To use ipypmolstar as panel AnyWidgetComponent, the panel package needs to be installed"
10 | raise ImportError(msg)
11 | import param
12 | from panel.custom import AnyWidgetComponent
13 |
14 |
15 | class PDBeMolstar(AnyWidgetComponent):
16 | _esm = pathlib.Path(__file__).parent.parent / "static" / "pdbemolstar.js"
17 | _stylesheets = [
18 | str(pathlib.Path(__file__).parent.parent / "static" / "pdbe-light.css")
19 | ]
20 |
21 | width = param.String(default="800px")
22 | height = param.String(default="500px")
23 |
24 | molecule_id = param.String()
25 | custom_data = param.Dict(default=None, allow_None=True)
26 | assembly_id = param.String(default="")
27 | default_preset = param.Selector(
28 | default="default", objects=["default", "unitcell", "all-models", "supercell"]
29 | )
30 | ligand_view = param.Dict(default=None)
31 | alphafold_view = param.Boolean(default=False)
32 | superposition = param.Boolean(default=False)
33 | superposition_params = param.Dict(default=None)
34 | visual_style = param.Selector(
35 | default=None,
36 | objects=[
37 | "cartoon",
38 | "ball-and-stick",
39 | "carbohydrate",
40 | "ellipsoid",
41 | "gaussian-surface",
42 | "molecular-surface",
43 | "point",
44 | "putty",
45 | "spacefill",
46 | ],
47 | )
48 | hide_polymer = param.Boolean(default=False)
49 | hide_water = param.Boolean(default=False)
50 | hide_heteroatoms = param.Boolean(default=False)
51 | hide_carbs = param.Boolean(default=False)
52 | hide_non_standard = param.Boolean(default=False)
53 | hide_coarse = param.Boolean(default=False)
54 | load_maps = param.Boolean(default=False)
55 | map_settings = param.Dict(default=None)
56 | bg_color = param.String(default="")
57 | highlight_color = param.String(default="#FF6699")
58 | select_color = param.String(default="#33FF19")
59 | lighting = param.Selector(
60 | default=None, objects=["flat", "matte", "glossy", "metallic", "plastic"]
61 | )
62 | validation_annotation = param.Boolean(default=False)
63 | domain_annotation = param.Boolean(default=False)
64 | symmetry_annotation = param.Boolean(default=False)
65 | pdbe_url = param.String(default="https://www.ebi.ac.uk/pdbe/")
66 | encoding = param.Selector(default="bcif", objects=["bcif", "cif"])
67 | low_precision_coords = param.Boolean(default=False)
68 | select_interaction = param.Boolean(default=True)
69 | granularity = param.Selector(
70 | default="residue",
71 | objects=[
72 | "element",
73 | "residue",
74 | "chain",
75 | "entity",
76 | "model",
77 | "operator",
78 | "structure",
79 | "elementInstances",
80 | "residueInstances",
81 | "chainInstances",
82 | ],
83 | )
84 | subscribe_events = param.Boolean(default=False)
85 | hide_controls = param.Boolean(default=True)
86 | hide_controls_icon = param.Boolean(default=False)
87 | hide_expand_icon = param.Boolean(default=False)
88 | hide_settings_icon = param.Boolean(default=False)
89 | hide_selection_icon = param.Boolean(default=False)
90 | hide_animation_icon = param.Boolean(default=False)
91 | sequence_panel = param.Boolean(default=False)
92 | pdbe_link = param.Boolean(default=True)
93 | loading_overlay = param.Boolean(default=False)
94 | expanded = param.Boolean(default=False)
95 | landscape = param.Boolean(default=False)
96 | reactive = param.Boolean(default=False)
97 |
98 | spin = param.Boolean(default=False)
99 | _focus = param.List(default=None)
100 | highlight = param.Dict(default=None)
101 | _clear_highlight = param.Boolean(default=False)
102 | color_data = param.Dict(default=None)
103 | _clear_selection = param.Boolean(default=False)
104 | tooltips = param.Dict(default=None)
105 | _clear_tooltips = param.Boolean(default=False)
106 | _set_color = param.Dict(default=None)
107 | _reset = param.Dict(default=None)
108 | _update = param.Dict(default=None)
109 | _args = param.Dict(default={})
110 |
111 | mouseover_event = param.Dict(default={})
112 | mouseout_event = param.Boolean(default=False)
113 | click_event = param.Dict(default={})
114 | click_focus = param.Boolean(default=True)
115 |
116 | def __init__(self, theme="light", **params):
117 | _stylesheets = [THEMES[theme]["css"]]
118 | bg_color = params.pop("bg_color", THEMES[theme]["bg_color"])
119 | self._stylesheets = _stylesheets # shouldnt work but it does
120 |
121 | super().__init__(bg_color=bg_color, **params)
122 |
123 | def color(
124 | self,
125 | data: list[QueryParam],
126 | non_selected_color=None,
127 | keep_colors=False,
128 | keep_representations=False,
129 | ) -> None:
130 | """
131 | Alias for PDBE Molstar's `select` method.
132 |
133 | See https://github.com/molstar/pdbe-molstar/wiki/3.-Helper-Methods for parameter
134 | details
135 | """
136 |
137 | self.color_data = {
138 | "data": data,
139 | "nonSelectedColor": non_selected_color,
140 | "keepColors": keep_colors,
141 | "keepRepresentations": keep_representations,
142 | }
143 | self.color_data = None
144 |
145 | def focus(self, data: list[QueryParam]):
146 | self._focus = data
147 | self._focus = None
148 |
149 | def clear_highlight(self):
150 | self._clear_highlight = not self._clear_highlight
151 |
152 | def clear_tooltips(self):
153 | self._clear_tooltips = not self._clear_tooltips
154 |
155 | def clear_selection(self, structure_number=None):
156 | # move payload to the traitlet which triggers the callback
157 | self._args = {"number": structure_number}
158 | self._clear_selection = not self._clear_selection
159 |
160 | # todo make two traits: select_color, hightlight_color
161 | def set_color(
162 | self, highlight: Optional[Color] = None, select: Optional[Color] = None
163 | ):
164 | data = {}
165 | if highlight is not None:
166 | data["highlight"] = highlight
167 | if select is not None:
168 | data["select"] = select
169 | if data:
170 | self._set_color = data
171 | self._set_color = None
172 |
173 | def reset(self, data: ResetParam):
174 | self._reset = data
175 | self._reset = None
176 |
177 | def update(self, data):
178 | self._update = data
179 | self._update = None
180 |
--------------------------------------------------------------------------------
/examples/molviewspec/double_hooke.py:
--------------------------------------------------------------------------------
1 | """
2 | This is an example from the Kingdon Teahouse (https://github.com/tBuLi/teahouse); adapted for ipymolstar.
3 |
4 | Original example is from the ["May The Forque Be With You"](https://enki.ws/ganja.js/examples/pga_dyn.html) implementation supplement to [PGAdyn](https://bivector.net/PGADYN.html).
5 |
6 | """
7 |
8 | from ipymolstar.molviewspec import DEFAULT_MVS_LOAD_OPTIONS, MolViewSpec
9 | from molviewspec import create_builder, States, GlobalMetadata
10 | from kingdon import Algebra, MultiVector
11 |
12 | from functools import reduce
13 | from operator import add
14 |
15 |
16 | alg = Algebra(3, 0, 1)
17 | globals().update(alg.blades)
18 | bds = alg.blades
19 | # %%
20 |
21 |
22 | def rsum(values):
23 | return reduce(add, values)
24 |
25 |
26 | def dist_pp(P1, P2) -> float: # point to point
27 | return (P1.normalized() & P2.normalized()).norm().e
28 |
29 |
30 | def sort_nn_dist(points: list[MultiVector]) -> list[MultiVector]:
31 | """Sort points by nearest neighbor."""
32 | p_list = points.copy()
33 | current = p_list.pop(0)
34 | output = [current]
35 | while p_list:
36 | next = min(p_list, key=lambda p: dist_pp(current, p))
37 | output.append(next)
38 | p_list.remove(next)
39 | current = next
40 | return output
41 |
42 |
43 | def xyz(point: MultiVector) -> tuple:
44 | """Convert a MultiVector to a tuple."""
45 | return tuple(point.undual().values()[1:])
46 |
47 |
48 | class Scene:
49 | def __init__(self):
50 | self.builder = create_builder()
51 |
52 | def append(self, mv: MultiVector | list[MultiVector], **kwargs):
53 | """Append a MultiVector to the scene."""
54 | # self.builder.append(mv, **kwargs)
55 | if isinstance(mv, list):
56 | # all points
57 | if all(m.grades == (3,) for m in mv):
58 | if len(mv) == 2:
59 | # line
60 | points = [tuple(m.undual().values()[1:]) for m in mv]
61 | self.line(points[0], points[1], **kwargs)
62 | if len(mv) == 3:
63 | points = [tuple(m.undual().values()[1:]) for m in mv]
64 | self.triangle(points, **kwargs)
65 | if len(mv) == 4:
66 | self.quadrilateral(mv, **kwargs)
67 |
68 | elif isinstance(mv, MultiVector):
69 | # single point
70 | if mv.grades == (3,):
71 | coords = tuple(mv.undual().values()[1:])
72 | self.point(*coords, **kwargs)
73 |
74 | def quadrilateral(self, points: list[MultiVector], color="blue", **kwargs):
75 | p_sort = sort_nn_dist(points)
76 | vertices = [c for mv in p_sort for c in xyz(mv)]
77 | indices = [0, 1, 2, 0, 2, 3, 2, 1, 0, 3, 2, 0]
78 | self.primitives().mesh(
79 | vertices=vertices,
80 | indices=indices,
81 | triangle_groups=[0], # All triangles belong to the same group
82 | color=color,
83 | **kwargs,
84 | )
85 |
86 | def primitives(self, **kwargs):
87 | return self.builder.primitives(**kwargs)
88 |
89 | def axis_arrows(self, opacity=1.0):
90 | return (
91 | self.primitives(opacity=opacity)
92 | .arrow(
93 | start=(0, 0, 0),
94 | end=(1, 0, 0),
95 | color="red",
96 | tooltip="X",
97 | show_end_cap=True,
98 | )
99 | .arrow(
100 | start=(0, 0, 0),
101 | end=(0, 1, 0),
102 | color="green",
103 | tooltip="Y",
104 | show_end_cap=True,
105 | )
106 | .arrow(
107 | start=(0, 0, 0),
108 | end=(0, 0, 1),
109 | color="blue",
110 | tooltip="Z",
111 | show_end_cap=True,
112 | )
113 | )
114 |
115 | def point(self, x, y, z, size=0.1, color="red", **kwargs):
116 | """Create a point in 3D space."""
117 | return self.primitives().sphere(
118 | center=(x, y, z), radius=size, color=color, **kwargs
119 | )
120 |
121 | def line(self, start: tuple, end: tuple, size=0.02, color="black"):
122 | return self.primitives().tube(
123 | start=start,
124 | end=end,
125 | color=color,
126 | radius=size,
127 | )
128 |
129 | def triangle(self, points: list[tuple], color="blue"):
130 | """Create a triangle in 3D space."""
131 | return self.primitives().mesh(
132 | vertices=[coord for point in points for coord in point],
133 | indices=[0, 1, 2, 2, 1, 0],
134 | triangle_groups=[0],
135 | color=color,
136 | show_triangles=True,
137 | show_wireframe=False,
138 | )
139 |
140 | @property
141 | def msvj_data(self):
142 | return self.builder.get_state()
143 |
144 |
145 | # %%
146 |
147 |
148 | def RK4(f, y, h):
149 | k1 = f(*y)
150 | k2 = f(*[yi + 0.5 * h * k1i for yi, k1i in zip(y, k1)])
151 | k3 = f(*[yi + 0.5 * h * k2i for yi, k2i in zip(y, k2)])
152 | k4 = f(*[yi + h * k3i for yi, k3i in zip(y, k3)])
153 | return [
154 | yi + (h / 3) * (k2i + k3i + (k1i + k4i) * 0.5)
155 | for yi, k1i, k2i, k3i, k4i in zip(y, k1, k2, k3, k4)
156 | ]
157 |
158 |
159 | # %%
160 |
161 | d = 3
162 | size = [0.2, 1, 0.2]
163 | vertexes = [
164 | alg.vector([1, *[s * (((i >> j) % 2) - 0.5) for j, s in enumerate(size)]]).dual()
165 | for i in range(2**d)
166 | ]
167 |
168 | attach_1 = vertexes[5] + alg.blades.e2.dual()
169 | attach_2 = vertexes[1] + alg.blades.e2.dual() + 0.5 * alg.blades.e1.dual()
170 |
171 | initial_state = [1 - 0.5 * bds.e02, 38.35 * bds.e13 + 35.27 * bds.e12 - 5 * bds.e01]
172 |
173 |
174 | faces = [
175 | (0, 1, 2, 3),
176 | (4, 5, 6, 7),
177 | (0, 1, 4, 5),
178 | (2, 3, 6, 7),
179 | (0, 2, 4, 6),
180 | (1, 3, 5, 7),
181 | ]
182 |
183 | face_colors = ["CC00FF", "0400ff", "fbff00", "ffa200", "44ff00", "00aaff"]
184 |
185 |
186 | mass = 1
187 | I = (
188 | 1
189 | / 12
190 | * mass
191 | * (
192 | (size[1] ** 2 + size[2] ** 2) * bds.e01
193 | + (size[2] ** 2 + size[0] ** 2) * bds.e02
194 | + (size[0] ** 2 + size[1] ** 2) * bds.e03
195 | + 12 * bds.e12
196 | + 12 * bds.e13
197 | + 12 * bds.e23
198 | )
199 | )
200 |
201 | A = lambda B: B.dual().map(lambda k, v: v * getattr(I, alg.bin2canon[k]))
202 | Ai = lambda B: B.map(lambda k, v: v / getattr(I, alg.bin2canon[k])).undual()
203 |
204 |
205 | @alg.register(symbolic=True)
206 | def forques(M, B):
207 | Gravity = (~M >> -9.81 * bds.e02).dual()
208 | Damping = -0.25 * B.grade(2).dual()
209 | Hooke = -8 * (~M >> attach_1) & vertexes[5]
210 | Hooke2 = -8 * (~M >> attach_2) & vertexes[1]
211 | return (Gravity + Hooke + Hooke2 + Damping).grade(
212 | 2
213 | ) # Ensure a pure bivector because in kingdon<=1.1.0, >> doesn't. Maybe this will change in the future.
214 |
215 |
216 | # Change in M and B
217 | dState = lambda M, B: [-0.5 * M * B, Ai(forques(M, B) - A(B).cp(B))]
218 |
219 | # %%
220 | metadata = GlobalMetadata(
221 | title="beam_hook_law",
222 | description="a block dangling from two strings",
223 | description_format=None,
224 | )
225 | snapshots = []
226 |
227 | state = initial_state
228 |
229 | for i in range(500):
230 | scene = Scene()
231 | upd_vertexes = [state[0] >> point for point in vertexes]
232 |
233 | for c, face in zip(face_colors, faces):
234 | f_vertices = [upd_vertexes[i] for i in face]
235 | scene.quadrilateral(f_vertices, color=f"#{c.lower()}")
236 |
237 | scene.point(
238 | *xyz(attach_1.normalized()), size=0.1, color="green", tooltip="attach_1"
239 | )
240 | scene.point(
241 | *xyz(attach_2.normalized()), size=0.1, color="green", tooltip="attach_2"
242 | )
243 |
244 | scene.append([attach_1, upd_vertexes[0]], color="black", size=0.02)
245 | scene.append([attach_2, upd_vertexes[1]], color="black", size=0.02)
246 |
247 | snapshot = scene.builder.get_snapshot(
248 | title=str(i), linger_duration_ms=2, transition_duration_ms=2
249 | )
250 | snapshots.append(snapshot)
251 |
252 | # update the state
253 | state = RK4(dState, state, 0.01)
254 |
255 | states = States(
256 | snapshots=snapshots,
257 | metadata=metadata,
258 | ).dumps()
259 |
260 |
261 | # %%
262 | mvs_load_options = {"keepSnapshotCamera": True} | DEFAULT_MVS_LOAD_OPTIONS
263 | view = MolViewSpec(msvj_data=states, mvs_load_options=mvs_load_options)
264 | view
265 |
266 |
267 | # %%
268 |
--------------------------------------------------------------------------------
/src/ipymolstar/static/pdbemolstar.js:
--------------------------------------------------------------------------------
1 | import "https://cdn.jsdelivr.net/npm/pdbe-molstar@3.3.2/build/pdbe-molstar-plugin.js";
2 |
3 | function standardize_color(str) {
4 | var ctx = document.createElement("canvas").getContext("2d");
5 | ctx.fillStyle = str;
6 | return ctx.fillStyle;
7 | }
8 | function toRgb(color) {
9 | var hex = standardize_color(color);
10 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
11 | return result
12 | ? {
13 | r: parseInt(result[1], 16),
14 | g: parseInt(result[2], 16),
15 | b: parseInt(result[3], 16),
16 | }
17 | : null;
18 | }
19 |
20 | function getHideStructure(model) {
21 | var hideStructure = [];
22 |
23 | if (model.get("hide_polymer")) {
24 | hideStructure.push("polymer");
25 | }
26 | if (model.get("hide_water")) {
27 | hideStructure.push("water");
28 | }
29 | if (model.get("hide_heteroatoms")) {
30 | hideStructure.push("het");
31 | }
32 | if (model.get("hide_carbs")) {
33 | hideStructure.push("carbs");
34 | }
35 | if (model.get("hide_non_standard")) {
36 | hideStructure.push("nonStandard");
37 | }
38 | if (model.get("hide_coarse")) {
39 | hideStructure.push("coarse");
40 | }
41 |
42 | return hideStructure;
43 | }
44 |
45 | function getVisibility(model) {
46 | var visibility = {
47 | polymer: !model.get("hide_polymer"),
48 | het: !model.get("hide_heteroatoms"),
49 | water: !model.get("hide_water"),
50 | carbs: !model.get("hide_carbs"),
51 | // maps ?
52 | };
53 |
54 | return visibility;
55 | }
56 |
57 | function getHideCanvasControls(model) {
58 | var hideCanvasControls = [];
59 | if (model.get("hide_controls_icon")) {
60 | hideCanvasControls.push("controlToggle");
61 | }
62 | if (model.get("hide_expand_icon")) {
63 | hideCanvasControls.push("expand");
64 | }
65 | if (model.get("hide_settings_icon")) {
66 | hideCanvasControls.push("controlInfo");
67 | }
68 | if (model.get("hide_selection_icon")) {
69 | hideCanvasControls.push("selection");
70 | }
71 | if (model.get("hide_animation_icon")) {
72 | hideCanvasControls.push("animation");
73 | }
74 |
75 | return hideCanvasControls;
76 | }
77 |
78 | function getCustomData(model) {
79 | var customData = model.get("custom_data");
80 |
81 | if (customData && 'data' in customData) {
82 | var url = URL.createObjectURL(new Blob([customData.data]));
83 | customData.url = url;
84 | delete customData.data;
85 | }
86 |
87 | return customData;
88 | }
89 |
90 | const EmptyFocusBindings = {
91 | clickCenterFocus: { triggers: [], action: '', description: '' },
92 | clickCenterFocusSelectMode: { triggers: [], action: '', description: '' },
93 | clickResetCameraOnEmpty: { triggers: [], action: '', description: '' },
94 | clickResetCameraOnEmptySelectMode: { triggers: [], action: '', description: '' }
95 | };
96 |
97 |
98 | function getOptions(model) {
99 | var options = {
100 | moleculeId: model.get("molecule_id"),
101 | customData: getCustomData(model),
102 | assemblyId: model.get("assembly_id"),
103 | defaultPreset: model.get("default_preset"),
104 | ligandView: model.get("ligand_view"),
105 | alphafoldView: model.get("alphafold_view"),
106 | superposition: model.get("superposition"),
107 | superpositionParams: model.get("superposition_params"),
108 | visualStyle: model.get("visual_style"),
109 | loadMaps: model.get("load_maps"),
110 | bgColor: toRgb(model.get("bg_color")),
111 | selectColor: toRgb(model.get("select_color")),
112 | lighting: model.get("lighting"),
113 | validationAnnotation: model.get("validation_annotation"),
114 | symmetryAnnotation: model.get("symmetry_annotation"),
115 | pdbeUrl: model.get("pdbe_url"),
116 | encoding: model.get("encoding"),
117 | lowPrecisionCoords: model.get("low_precision_coords"),
118 | selectInteraction: model.get("select_interaction"),
119 | // selectBindings: EmptySelectBindings,
120 | granularity: model.get("granularity"),
121 | subscribeEvents: model.get("subscribe_events"),
122 | hideControls: model.get("hide_controls"),
123 | hideCanvasControls: getHideCanvasControls(model),
124 | sequencePanel: model.get("sequence_panel"),
125 | pdbeLink: model.get("pdbe_link"),
126 | loadingOverlay: model.get("loading_overlay"),
127 | expanded: model.get("expanded"),
128 | landscape: model.get("landscape"),
129 | reactive: model.get("reactive"),
130 | };
131 |
132 | if (model.get('click_focus') == false) {
133 | options.focusBindings = EmptyFocusBindings;
134 | }
135 |
136 | return options;
137 | }
138 |
139 | function subscribe(model, name, callback) {
140 | model.on(name, callback);
141 | return () => model.off(name, callback);
142 | }
143 |
144 | function render({ model, el }) {
145 | let viewerContainer = document.createElement("div");
146 | viewerContainer.id = "viewer_container";
147 |
148 | viewerContainer.style.height = model.get("height");
149 | viewerContainer.style.width = model.get("width");
150 |
151 | // Make the container responsive
152 | viewerContainer.style.maxWidth = "100%";
153 | viewerContainer.style.boxSizing = "border-box";
154 |
155 | var viewerInstance = new window.PDBeMolstarPlugin();
156 | viewerInstance.render(viewerContainer, getOptions(model)); //.then(() => {
157 | el.appendChild(viewerContainer);
158 |
159 | // callbacks to be called after loading is complete
160 | let callbacksLoadComplete = {
161 | "change:spin": () => viewerInstance.visual.toggleSpin(model.get("spin")),
162 | "change:hide_polymer": () => {
163 | viewerInstance.visual.visibility({ polymer: !model.get("hide_polymer") });
164 | },
165 | "change:hide_water": () => {
166 | viewerInstance.visual.visibility({ water: !model.get("hide_water") });
167 | },
168 | "change:hide_heteroatoms": () => {
169 | viewerInstance.visual.visibility({ het: !model.get("hide_heteroatoms") });
170 | },
171 | "change:hide_carbs": () => {
172 | viewerInstance.visual.visibility({ carbs: !model.get("hide_carbs") });
173 | },
174 | "change:hide_non_standard": () => {
175 | viewerInstance.visual.visibility({
176 | nonStandard: !model.get("hide_non_standard"),
177 | });
178 | },
179 | "change:hide_coarse": () => {
180 | viewerInstance.visual.visibility({ coarse: !model.get("hide_coarse") });
181 | },
182 | "change:color_data": () => {
183 | const selectValue = model.get("color_data");
184 | if (selectValue !== null) {
185 | viewerInstance.visual.select(selectValue);
186 | }
187 | },
188 | "change:highlight": () => {
189 | const highlightValue = model.get("highlight");
190 | if (highlightValue !== null) {
191 | viewerInstance.visual.highlight(highlightValue);
192 | }
193 | },
194 | "change:highlight_color": () => {
195 | const highlightColorValue = model.get("highlight_color");
196 | if (highlightColorValue !== null) {
197 | viewerInstance.visual.setColor({ highlight: highlightColorValue });
198 | }
199 | },
200 | "change:tooltips": () => {
201 | const tooltipValue = model.get("tooltips");
202 | if (tooltipValue !== null) {
203 | viewerInstance.visual.tooltips(tooltipValue);
204 | }
205 | },
206 | };
207 |
208 | let otherCallbacks = {
209 | "change:molecule_id": () => {
210 | viewerInstance.visual.update(getOptions(model), true);
211 | },
212 | "change:custom_data": () => {
213 | viewerInstance.visual.update(getOptions(model), true);
214 | },
215 | "change:visual_style": () => {
216 | viewerInstance.visual.update(getOptions(model), true);
217 | },
218 | "change:expanded": () => {
219 | viewerInstance.canvas.toggleExpanded(model.get("expanded"));
220 | },
221 | "change:bg_color": () => {
222 | viewerInstance.canvas.setBgColor(toRgb(model.get("bg_color")));
223 | },
224 | "change:_reset": () => {
225 | const resetValue = model.get("_reset");
226 | if (resetValue !== null) {
227 | viewerInstance.visual.reset(resetValue);
228 | }
229 | },
230 | "change:_clear_tooltips": () => {
231 | viewerInstance.visual.clearTooltips();
232 | }
233 | };
234 |
235 | let combinedCallbacks = Object.assign(
236 | {},
237 | callbacksLoadComplete,
238 | otherCallbacks
239 | );
240 |
241 | viewerInstance.events.loadComplete.subscribe(() => {
242 | // trigger callabacks which need to be called after loading
243 | Object.values(callbacksLoadComplete).forEach((callback) => callback());
244 | });
245 |
246 | // subscribe to events and collect unsubscribe funcs
247 | let unsubscribes = Object.entries(combinedCallbacks).map(([name, callback]) =>
248 | subscribe(model, name, callback)
249 | );
250 |
251 | document.addEventListener("PDB.molstar.mouseover", (e) => {
252 | const eventData = e.eventData;
253 | model.set("mouseover_event", eventData);
254 | model.save_changes();
255 | });
256 |
257 | document.addEventListener("PDB.molstar.mouseout", (e) => {
258 | model.set("mouseout_event", !model.get("mouseout_event") );
259 | model.save_changes();
260 | });
261 |
262 | document.addEventListener("PDB.molstar.click", (e) => {
263 | const eventData = e.eventData;
264 | model.set("click_event", eventData);
265 | model.save_changes();
266 | });
267 |
268 | return () => {
269 | unsubscribes.forEach((unsubscribe) => unsubscribe());
270 | };
271 | }
272 |
273 | export default { render };
274 |
--------------------------------------------------------------------------------
/src/ipymolstar/pdbemolstar.py:
--------------------------------------------------------------------------------
1 | import pathlib
2 | from typing import Any, List, Optional, TypedDict
3 |
4 | import anywidget
5 | import traitlets
6 |
7 | THEMES = {
8 | "light": {
9 | "bg_color": "#F7F7F7",
10 | "css": (
11 | pathlib.Path(__file__).parent / "static" / "pdbe-light.css"
12 | ).read_text(),
13 | },
14 | "dark": {
15 | "bg_color": "#111111",
16 | "css": (pathlib.Path(__file__).parent / "static" / "pdbe-dark.css").read_text(),
17 | },
18 | }
19 |
20 |
21 | class Color(TypedDict):
22 | r: int
23 | g: int
24 | b: int
25 |
26 |
27 | # codeieum translation of QueryParam from
28 | # https://github.com/molstar/pdbe-molstar/blob/master/src/app/helpers.ts#L180
29 | class QueryParam(TypedDict, total=False):
30 | auth_seq_id: Optional[int]
31 | entity_id: Optional[str]
32 | auth_asym_id: Optional[str]
33 | struct_asym_id: Optional[str]
34 | residue_number: Optional[int]
35 | start_residue_number: Optional[int]
36 | end_residue_number: Optional[int]
37 | auth_residue_number: Optional[int]
38 | auth_ins_code_id: Optional[str]
39 | start_auth_residue_number: Optional[int]
40 | start_auth_ins_code_id: Optional[str]
41 | end_auth_residue_number: Optional[int]
42 | end_auth_ins_code_id: Optional[str]
43 | atoms: Optional[List[str]]
44 | label_comp_id: Optional[str]
45 | color: Optional[Color]
46 | sideChain: Optional[bool]
47 | representation: Optional[str]
48 | representationColor: Optional[Color]
49 | focus: Optional[bool]
50 | tooltip: Optional[str]
51 | start: Optional[Any]
52 | end: Optional[Any]
53 | atom_id: Optional[List[int]]
54 | uniprot_accession: Optional[str]
55 | uniprot_residue_number: Optional[int]
56 | start_uniprot_residue_number: Optional[int]
57 | end_uniprot_residue_number: Optional[int]
58 |
59 |
60 | class ResetParam(TypedDict, total=False):
61 | camera: Optional[bool]
62 | theme: Optional[bool]
63 | highlightColor: Optional[bool]
64 | selectColor: Optional[bool]
65 |
66 |
67 | class PDBeMolstar(anywidget.AnyWidget):
68 | _esm = pathlib.Path(__file__).parent / "static" / "pdbemolstar.js"
69 | _css = pathlib.Path(__file__).parent / "static" / "pdbe-light.css"
70 |
71 | width = traitlets.Unicode("100%").tag(sync=True)
72 | height = traitlets.Unicode("500px").tag(sync=True)
73 |
74 | molecule_id = traitlets.Unicode().tag(sync=True)
75 | custom_data = traitlets.Dict(default_value=None, allow_none=True).tag(sync=True)
76 | assembly_id = traitlets.Unicode().tag(sync=True)
77 | default_preset = traitlets.Enum(
78 | ["default", "unitcell", "all-models", "supercell"],
79 | default_value="default",
80 | ).tag(sync=True)
81 | ligand_view = traitlets.Dict(default_value=None, allow_none=True).tag(sync=True)
82 | alphafold_view = traitlets.Bool(default_value=False).tag(sync=True)
83 | superposition = traitlets.Bool(default_value=False).tag(sync=True)
84 | superposition_params = traitlets.Dict(default_value=None, allow_none=True).tag(
85 | sync=True
86 | )
87 | visual_style = traitlets.Enum(
88 | [
89 | "cartoon",
90 | "ball-and-stick",
91 | "carbohydrate",
92 | "ellipsoid",
93 | "gaussian-surface",
94 | "molecular-surface",
95 | "point",
96 | "putty",
97 | "spacefill",
98 | ],
99 | default_value=None,
100 | allow_none=True,
101 | ).tag(sync=True)
102 | hide_polymer = traitlets.Bool(False).tag(sync=True)
103 | hide_water = traitlets.Bool(False).tag(sync=True)
104 | hide_heteroatoms = traitlets.Bool(False).tag(sync=True)
105 | hide_carbs = traitlets.Bool(False).tag(sync=True)
106 | hide_non_standard = traitlets.Bool(False).tag(sync=True)
107 | hide_coarse = traitlets.Bool(False).tag(sync=True)
108 | load_maps = traitlets.Bool(False).tag(sync=True)
109 | map_settings = traitlets.Dict(default_value=None, allow_none=True).tag(sync=True)
110 | bg_color = traitlets.Unicode().tag(sync=True)
111 | highlight_color = traitlets.Unicode("#FF6699").tag(sync=True)
112 | select_color = traitlets.Unicode("#33FF19").tag(sync=True)
113 | lighting = traitlets.Enum(
114 | ["flat", "matte", "glossy", "metallic", "plastic"],
115 | default_value=None,
116 | allow_none=True,
117 | ).tag(sync=True)
118 | validation_annotation = traitlets.Bool(False).tag(sync=True)
119 | domain_annotation = traitlets.Bool(False).tag(sync=True)
120 | symmetry_annotation = traitlets.Bool(False).tag(sync=True)
121 | pdbe_url = traitlets.Unicode("https://www.ebi.ac.uk/pdbe/").tag(sync=True)
122 | encoding = traitlets.Enum(["bcif", "cif"], default_value="bcif").tag(sync=True)
123 | low_precision_coords = traitlets.Bool(False).tag(sync=True)
124 | select_interaction = traitlets.Bool(True).tag(sync=True)
125 | granularity = traitlets.Enum(
126 | [
127 | "element",
128 | "residue",
129 | "chain",
130 | "entity",
131 | "model",
132 | "operator",
133 | "structure",
134 | "elementInstances",
135 | "residueInstances",
136 | "chainInstances",
137 | ],
138 | default_value="residue",
139 | ).tag(sync=True)
140 | subscribe_events = traitlets.Bool(False).tag(sync=True)
141 | hide_controls = traitlets.Bool(True).tag(sync=True)
142 | hide_controls_icon = traitlets.Bool(False).tag(sync=True)
143 | hide_expand_icon = traitlets.Bool(False).tag(sync=True)
144 | hide_settings_icon = traitlets.Bool(False).tag(sync=True)
145 | hide_selection_icon = traitlets.Bool(False).tag(sync=True)
146 | hide_animation_icon = traitlets.Bool(False).tag(sync=True)
147 | sequence_panel = traitlets.Bool(False).tag(sync=True)
148 | pdbe_link = traitlets.Bool(True).tag(sync=True)
149 | loading_overlay = traitlets.Bool(False).tag(sync=True)
150 | expanded = traitlets.Bool(False).tag(sync=True)
151 | landscape = traitlets.Bool(False).tag(sync=True)
152 | reactive = traitlets.Bool(False).tag(sync=True)
153 |
154 | spin = traitlets.Bool(False).tag(sync=True)
155 | _focus = traitlets.List(default_value=None, allow_none=True).tag(sync=True)
156 | highlight = traitlets.Dict(default_value=None, allow_none=True).tag(sync=True)
157 | _clear_highlight = traitlets.Bool(default_value=False).tag(sync=True)
158 | color_data = traitlets.Dict(default_value=None, allow_none=True).tag(sync=True)
159 | _clear_selection = traitlets.Bool(default_value=False).tag(sync=True)
160 | tooltips = traitlets.Dict(default_value=None, allow_none=True).tag(sync=True)
161 | _clear_tooltips = traitlets.Bool(default_value=False).tag(sync=True)
162 | _set_color = traitlets.Dict(default_value=None, allow_none=True).tag(sync=True)
163 | _reset = traitlets.Dict(allow_none=True, default_value=None).tag(sync=True)
164 | _update = traitlets.Dict(allow_none=True, default_value=None).tag(sync=True)
165 |
166 | _args = traitlets.Dict().tag(sync=True)
167 |
168 | mouseover_event = traitlets.Dict().tag(sync=True)
169 | mouseout_event = traitlets.Bool().tag(sync=True)
170 | click_event = traitlets.Dict().tag(sync=True)
171 | click_focus = traitlets.Bool(True).tag(sync=True)
172 |
173 | def __init__(self, theme="light", **kwargs):
174 | _css = THEMES[theme]["css"]
175 | bg_color = kwargs.pop("bg_color", THEMES[theme]["bg_color"])
176 | super().__init__(_css=_css, bg_color=bg_color, **kwargs)
177 |
178 | def color(
179 | self,
180 | data: list[QueryParam],
181 | non_selected_color=None,
182 | keep_colors=False,
183 | keep_representations=False,
184 | ) -> None:
185 | """
186 | Alias for PDBE Molstar's `select` method.
187 |
188 | See https://github.com/molstar/pdbe-molstar/wiki/3.-Helper-Methods for parameter
189 | details
190 | """
191 |
192 | self.color_data = {
193 | "data": data,
194 | "nonSelectedColor": non_selected_color,
195 | "keepColors": keep_colors,
196 | "keepRepresentations": keep_representations,
197 | }
198 | self.color_data = None
199 |
200 | def focus(self, data: list[QueryParam]):
201 | self._focus = data
202 | self._focus = None
203 |
204 | def clear_highlight(self):
205 | self._clear_highlight = not self._clear_highlight
206 |
207 | def clear_tooltips(self):
208 | self._clear_tooltips = not self._clear_tooltips
209 |
210 | def clear_selection(self, structure_number=None):
211 | # move payload to the traitlet which triggers the callback
212 | self._args = {"number": structure_number}
213 | self._clear_selection = not self._clear_selection
214 |
215 | # todo make two traits: select_color, hightlight_color
216 | def set_color(
217 | self, highlight: Optional[Color] = None, select: Optional[Color] = None
218 | ):
219 | data = {}
220 | if highlight is not None:
221 | data["highlight"] = highlight
222 | if select is not None:
223 | data["select"] = select
224 | if data:
225 | self._set_color = data
226 | self._set_color = None
227 |
228 | def reset(self, data: ResetParam):
229 | self._reset = data
230 | self._reset = None
231 |
232 | def update(self, data):
233 | self._update = data
234 | self._update = None
235 |
--------------------------------------------------------------------------------
/examples/pdbemolstar/panel_app.py:
--------------------------------------------------------------------------------
1 | import statistics
2 | from io import StringIO
3 |
4 | import panel as pn
5 | import param
6 | import requests
7 | from Bio.PDB import PDBParser, Residue, Structure
8 | from ipymolstar.panel import PDBeMolstar
9 | from matplotlib import colormaps
10 | from matplotlib.colors import Normalize
11 |
12 | theme = "light" if pn.config.theme == "default" else "dark"
13 |
14 | parser = PDBParser(QUIET=True)
15 | CHAIN_COLORS = [
16 | "#1f77b4",
17 | "#ff7f0e",
18 | "#2ca02c",
19 | "#d62728",
20 | "#9467bd",
21 | "#8c564b",
22 | "#e377c2",
23 | "#7f7f7f",
24 | "#bcbd22",
25 | "#17becf",
26 | ]
27 | AMINO_ACIDS = [
28 | "ALA",
29 | "ARG",
30 | "ASN",
31 | "ASP",
32 | "CYS",
33 | "GLN",
34 | "GLU",
35 | "GLY",
36 | "HIS",
37 | "ILE",
38 | "LEU",
39 | "LYS",
40 | "MET",
41 | "PHE",
42 | "PRO",
43 | "PYL",
44 | "SEC",
45 | "SER",
46 | "THR",
47 | "TRP",
48 | "TYR",
49 | "VAL",
50 | ]
51 | # use auth residue numbers or not
52 | AUTH_RESIDUE_NUMBERS = {
53 | "1QYN": False,
54 | "2PE4": True,
55 | }
56 |
57 | pdb_ids = ["1QYN", "2PE4"]
58 |
59 |
60 | def fetch_pdb(pdb_id) -> StringIO:
61 | url = f"https://files.rcsb.org/download/{pdb_id}.pdb"
62 | response = requests.get(url)
63 | if response.status_code == 200:
64 | sio = StringIO(response.text)
65 | sio.seek(0)
66 | return sio
67 | else:
68 | raise requests.HTTPError(f"Failed to download PDB file {pdb_id}")
69 |
70 |
71 | structures = {p_id: parser.get_structure(p_id, fetch_pdb(p_id)) for p_id in pdb_ids}
72 |
73 |
74 | def color_chains(structure: Structure.Structure) -> dict:
75 | data = [
76 | {
77 | "struct_asym_id": chain.id,
78 | "color": hex_color,
79 | }
80 | for hex_color, chain in zip(CHAIN_COLORS, structure.get_chains())
81 | ]
82 |
83 | color_data = {"data": data, "nonSelectedColor": None}
84 | return color_data
85 |
86 |
87 | def color_residues(structure: Structure.Structure, auth: bool = False) -> dict:
88 | _, resn, _ = zip(
89 | *[r.id for r in structure.get_residues() if r.get_resname() in AMINO_ACIDS]
90 | )
91 |
92 | rmin, rmax = min(resn), max(resn)
93 | norm = Normalize(vmin=rmin, vmax=rmax)
94 | auth_str = "auth_" if auth else ""
95 |
96 | cmap = colormaps["rainbow"]
97 | data = []
98 | for i in range(rmin, rmax):
99 | r, g, b, a = cmap(norm(i), bytes=True)
100 | color = {"r": int(r), "g": int(g), "b": int(b)}
101 | elem = {
102 | f"{auth_str}residue_number": i,
103 | "color": color,
104 | "focus": False,
105 | }
106 | data.append(elem)
107 |
108 | color_data = {"data": data, "nonSelectedColor": None}
109 | return color_data
110 |
111 |
112 | def get_bfactor(residue: Residue.Residue):
113 | """returns the residue-average b-factor"""
114 | return statistics.mean([atom.get_bfactor() for atom in residue])
115 |
116 |
117 | def color_bfactor(structure: Structure.Structure, auth: bool = False) -> dict:
118 | auth_str = "auth_" if auth else ""
119 | value_data = []
120 | for chain in structure.get_chains():
121 | for r in chain.get_residues():
122 | if r.get_resname() in AMINO_ACIDS:
123 | bfactor = get_bfactor(r)
124 | elem = {
125 | f"{auth_str}residue_number": r.id[1],
126 | "struct_asym_id": chain.id,
127 | "value": bfactor,
128 | }
129 | value_data.append(elem)
130 |
131 | all_values = [d["value"] for d in value_data]
132 | vmin, vmax = min(all_values), max(all_values)
133 |
134 | norm = Normalize(vmin=vmin, vmax=vmax)
135 | cmap = colormaps["inferno"]
136 | data = []
137 | for v_elem in value_data:
138 | elem = v_elem.copy()
139 | r, g, b, a = cmap(norm(elem.pop("value")), bytes=True)
140 | elem["color"] = {"r": int(r), "g": int(g), "b": int(b)}
141 | data.append(elem)
142 |
143 | color_data = {"data": data, "nonSelectedColor": None}
144 | return color_data
145 |
146 |
147 | def apply_coloring(pdb_id: str, color_mode: str):
148 | structure = structures[pdb_id]
149 | auth = AUTH_RESIDUE_NUMBERS[pdb_id]
150 | if color_mode == "Chain":
151 | return color_chains(structure)
152 | elif color_mode == "Residue":
153 | return color_residues(structure, auth)
154 | elif color_mode == "β-factor":
155 | return color_bfactor(structure, auth)
156 | else:
157 | raise ValueError(f"Invalid color mode: {color_mode}")
158 |
159 |
160 | protein_store = ["1QYN", "2PE4"]
161 | molecule_store = {
162 | "Glucose": dict(
163 | url="https://pubchem.ncbi.nlm.nih.gov/rest/pug/conformers/000016A100000001/SDF?response_type=save&response_basename=Conformer3D_COMPOUND_CID_5793",
164 | format="sdf",
165 | binary=False,
166 | ),
167 | "ATP": dict(
168 | url="https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/CID/5957/record/SDF?record_type=3d&response_type=save&response_basename=Conformer3D_COMPOUND_CID_5957",
169 | format="sdf",
170 | binary=False,
171 | ),
172 | "Caffeine": dict(
173 | url="https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/CID/2519/record/SDF?record_type=3d&response_type=save&response_basename=Conformer3D_COMPOUND_CID_2519",
174 | format="sdf",
175 | binary=False,
176 | ),
177 | "Strychnine": dict(
178 | url=" https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/CID/441071/record/SDF?record_type=3d&response_type=save&response_basename=Conformer3D_COMPOUND_CID_441071 ",
179 | format="sdf",
180 | binary=False,
181 | ),
182 | }
183 |
184 |
185 | class Controller(param.Parameterized):
186 | structure_type = param.Selector(
187 | default="Protein",
188 | allow_None=False,
189 | objects=["Protein", "Molecule"],
190 | doc="Choose to display protein or a small molecule",
191 | )
192 |
193 | protein_id = param.Selector(
194 | default=protein_store[0],
195 | objects=protein_store,
196 | doc="Protein to display",
197 | )
198 |
199 | molecule_id = param.Selector(
200 | default="Glucose",
201 | objects=list(molecule_store.keys()),
202 | doc="Molecule to display",
203 | )
204 |
205 | color_mode = param.Selector(
206 | default="Chain",
207 | objects=["Chain", "Residue", "β-factor"],
208 | doc="Coloring mode",
209 | )
210 |
211 | def __init__(self, molstar_view: PDBeMolstar, **params):
212 | self.molstar_view = molstar_view
213 | super().__init__(**params)
214 |
215 | @param.depends("structure_type")
216 | def secondary_selector(self):
217 | if self.structure_type == "Protein":
218 | return pn.widgets.Select.from_param(self.param.protein_id, name="Protein")
219 | elif self.structure_type == "Molecule":
220 | return pn.widgets.Select.from_param(self.param.molecule_id, name="Molecule")
221 | else:
222 | return pn.pane.Str("Please make a selection")
223 |
224 | @param.depends("structure_type")
225 | def color_selector(self):
226 | if self.structure_type == "Protein":
227 | return pn.widgets.Select.from_param(
228 | self.param.color_mode, name="Color mode"
229 | )
230 | return None
231 |
232 | @param.depends(
233 | "structure_type", "protein_id", "molecule_id", "color_mode", watch=True
234 | )
235 | def update_molecule_data(self):
236 | if self.structure_type == "Protein":
237 | molecule_id = self.protein_id.lower()
238 | custom_data = None
239 | color_data = apply_coloring(self.protein_id, self.color_mode)
240 | else:
241 | molecule_id = ""
242 | custom_data = molecule_store[self.molecule_id]
243 | color_data = {"data": [], "nonSelectedColor": None}
244 |
245 | self.molstar_view.param.update(
246 | molecule_id=molecule_id, custom_data=custom_data, color_data=color_data
247 | )
248 |
249 |
250 | color_data = apply_coloring(pdb_ids[0], "Chain") # initial value
251 | molstar = PDBeMolstar(
252 | molecule_id="1qyn", theme=theme, sizing_mode="stretch_width", color_data=color_data
253 | ) # , width="1150px")
254 |
255 |
256 | parameters = pn.Param(
257 | molstar,
258 | parameters=[
259 | "spin",
260 | "visual_style",
261 | "hide_water",
262 | "hide_polymer",
263 | "hide_heteroatoms",
264 | "hide_carbs",
265 | "hide_non_standard",
266 | "hide_coarse",
267 | "bg_color",
268 | ],
269 | widgets={
270 | "bg_color": {"type": pn.widgets.ColorPicker, "sizing_mode": "stretch_width"}
271 | },
272 | show_name=False,
273 | )
274 |
275 | ctrl = Controller(molstar)
276 |
277 |
278 | settings = pn.Column(
279 | pn.pane.Markdown("## Controls"),
280 | pn.widgets.Select.from_param(ctrl.param.structure_type, name="Structure type"),
281 | ctrl.secondary_selector,
282 | ctrl.color_selector,
283 | *parameters,
284 | )
285 | view = pn.Column(molstar, sizing_mode="stretch_width")
286 |
287 | template = pn.template.FastListTemplate(
288 | title="ipymolstar - Panel",
289 | sidebar=[settings],
290 | main_max_width="1200px",
291 | )
292 |
293 | template.main.append(view)
294 |
295 | if pn.state.served:
296 | template.servable()
297 |
--------------------------------------------------------------------------------
/examples/pdbemolstar/solara_app.py:
--------------------------------------------------------------------------------
1 | import statistics
2 | from dataclasses import asdict, dataclass
3 | from io import StringIO
4 | from pathlib import Path
5 |
6 | import requests
7 | import solara
8 | import solara.lab
9 | from Bio.PDB import PDBParser, Residue, Structure
10 | from ipymolstar import PDBeMolstar
11 | from ipymolstar.pdbemolstar import THEMES
12 | from matplotlib import colormaps
13 | from matplotlib.colors import Normalize
14 | from solara.alias import rv
15 |
16 | parser = PDBParser(QUIET=True)
17 | CHAIN_COLORS = [
18 | "#1f77b4",
19 | "#ff7f0e",
20 | "#2ca02c",
21 | "#d62728",
22 | "#9467bd",
23 | "#8c564b",
24 | "#e377c2",
25 | "#7f7f7f",
26 | "#bcbd22",
27 | "#17becf",
28 | ]
29 | AMINO_ACIDS = [
30 | "ALA",
31 | "ARG",
32 | "ASN",
33 | "ASP",
34 | "CYS",
35 | "GLN",
36 | "GLU",
37 | "GLY",
38 | "HIS",
39 | "ILE",
40 | "LEU",
41 | "LYS",
42 | "MET",
43 | "PHE",
44 | "PRO",
45 | "PYL",
46 | "SEC",
47 | "SER",
48 | "THR",
49 | "TRP",
50 | "TYR",
51 | "VAL",
52 | ]
53 | # use auth residue numbers or not
54 | AUTH_RESIDUE_NUMBERS = {
55 | "1QYN": False,
56 | "2PE4": True,
57 | }
58 |
59 | pdb_ids = ["1QYN", "2PE4"]
60 |
61 |
62 | def fetch_pdb(pdb_id) -> StringIO:
63 | url = f"https://files.rcsb.org/download/{pdb_id}.pdb"
64 | response = requests.get(url)
65 | if response.status_code == 200:
66 | sio = StringIO(response.text)
67 | sio.seek(0)
68 | return sio
69 | else:
70 | raise requests.HTTPError(f"Failed to download PDB file {pdb_id}")
71 |
72 |
73 | structures = {p_id: parser.get_structure(p_id, fetch_pdb(p_id)) for p_id in pdb_ids}
74 |
75 |
76 | @dataclass
77 | class PDBeData:
78 | molecule_id: str = "1qyn"
79 | custom_data: dict | None = None
80 | color_data: dict | None = None
81 | bg_color: str = "#F7F7F7"
82 | spin: bool = False
83 | hide_polymer: bool = False
84 | hide_water: bool = False
85 | hide_heteroatoms: bool = False
86 | hide_carbs: bool = False
87 | hide_non_standard: bool = False
88 | hide_coarse: bool = False
89 |
90 | height: str = "700px"
91 |
92 |
93 | visibility_cbs = ["polymer", "water", "heteroatoms", "carbs"]
94 |
95 |
96 | def color_chains(structure: Structure.Structure) -> dict:
97 | data = [
98 | {
99 | "struct_asym_id": chain.id,
100 | "color": hex_color,
101 | }
102 | for hex_color, chain in zip(CHAIN_COLORS, structure.get_chains())
103 | ]
104 |
105 | color_data = {"data": data, "nonSelectedColor": None}
106 | return color_data
107 |
108 |
109 | def color_residues(structure: Structure.Structure, auth: bool = False) -> dict:
110 | _, resn, _ = zip(
111 | *[r.id for r in structure.get_residues() if r.get_resname() in AMINO_ACIDS]
112 | )
113 |
114 | rmin, rmax = min(resn), max(resn)
115 | norm = Normalize(vmin=rmin, vmax=rmax)
116 | auth_str = "auth_" if auth else ""
117 |
118 | cmap = colormaps["rainbow"]
119 | data = []
120 | for i in range(rmin, rmax):
121 | r, g, b, a = cmap(norm(i), bytes=True)
122 | color = {"r": int(r), "g": int(g), "b": int(b)}
123 | elem = {
124 | f"{auth_str}residue_number": i,
125 | "color": color,
126 | "focus": False,
127 | }
128 | data.append(elem)
129 |
130 | color_data = {"data": data, "nonSelectedColor": None}
131 | return color_data
132 |
133 |
134 | def get_bfactor(residue: Residue.Residue):
135 | """returns the residue-average b-factor"""
136 | return statistics.mean([atom.get_bfactor() for atom in residue])
137 |
138 |
139 | def color_bfactor(structure: Structure.Structure, auth: bool = False) -> dict:
140 | auth_str = "auth_" if auth else ""
141 | value_data = []
142 | for chain in structure.get_chains():
143 | for r in chain.get_residues():
144 | if r.get_resname() in AMINO_ACIDS:
145 | bfactor = get_bfactor(r)
146 | elem = {
147 | f"{auth_str}residue_number": r.id[1],
148 | "struct_asym_id": chain.id,
149 | "value": bfactor,
150 | }
151 | value_data.append(elem)
152 |
153 | all_values = [d["value"] for d in value_data]
154 | vmin, vmax = min(all_values), max(all_values)
155 |
156 | norm = Normalize(vmin=vmin, vmax=vmax)
157 | cmap = colormaps["inferno"]
158 | data = []
159 | for v_elem in value_data:
160 | elem = v_elem.copy()
161 | r, g, b, a = cmap(norm(elem.pop("value")), bytes=True)
162 | elem["color"] = {"r": int(r), "g": int(g), "b": int(b)}
163 | data.append(elem)
164 |
165 | color_data = {"data": data, "nonSelectedColor": None}
166 | return color_data
167 |
168 |
169 | def apply_coloring(pdb_id: str, color_mode: str):
170 | structure = structures[pdb_id]
171 | auth = AUTH_RESIDUE_NUMBERS[pdb_id]
172 | if color_mode == "Chain":
173 | return color_chains(structure)
174 | elif color_mode == "Residue":
175 | return color_residues(structure, auth)
176 | elif color_mode == "β-factor":
177 | return color_bfactor(structure, auth)
178 | else:
179 | raise ValueError(f"Invalid color mode: {color_mode}")
180 |
181 |
182 | color_options = ["Chain", "Residue", "β-factor"]
183 | color_data = apply_coloring(pdb_ids[0], color_options[0])
184 | data = solara.Reactive(PDBeData(color_data=color_data))
185 |
186 | molecule_store = {
187 | "Glucose": dict(
188 | url="https://pubchem.ncbi.nlm.nih.gov/rest/pug/conformers/000016A100000001/SDF?response_type=save&response_basename=Conformer3D_COMPOUND_CID_5793",
189 | format="sdf",
190 | binary=False,
191 | ),
192 | "ATP": dict(
193 | url="https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/CID/5957/record/SDF?record_type=3d&response_type=save&response_basename=Conformer3D_COMPOUND_CID_5957",
194 | format="sdf",
195 | binary=False,
196 | ),
197 | "Caffeine": dict(
198 | url="https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/CID/2519/record/SDF?record_type=3d&response_type=save&response_basename=Conformer3D_COMPOUND_CID_2519",
199 | format="sdf",
200 | binary=False,
201 | ),
202 | "Strychnine": dict(
203 | url="https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/CID/441071/record/SDF?record_type=3d&response_type=save&response_basename=Conformer3D_COMPOUND_CID_441071 ",
204 | format="sdf",
205 | binary=False,
206 | ),
207 | }
208 |
209 |
210 | def download_pdb(pdb_id, fpath: Path):
211 | url = f"https://files.rcsb.org/download/{pdb_id}.pdb"
212 | response = requests.get(url)
213 | if response.status_code == 200:
214 | fpath.write_bytes(response.content)
215 | return f"{pdb_id}.pdb"
216 | else:
217 | print("Failed to download PDB file")
218 | return None
219 |
220 |
221 | @solara.component
222 | def ProteinView(dark_effective: bool):
223 | with solara.Card("PDBeMol*"):
224 | theme = "dark" if dark_effective else "light"
225 | PDBeMolstar.element(**asdict(data.value), theme=theme)
226 |
227 |
228 | @solara.component
229 | def Page():
230 | solara.Title("ipymolstar - Solara")
231 | counter, set_counter = solara.use_state(0)
232 | dark_effective = solara.lab.use_dark_effective()
233 | dark_effective_previous = solara.use_previous(dark_effective)
234 |
235 | structure_type = solara.use_reactive("Protein")
236 | color_mode = solara.use_reactive(color_options[0])
237 | protein_id = solara.use_reactive(pdb_ids[0])
238 | molecule_key = solara.use_reactive(next(iter(molecule_store.keys())))
239 |
240 | if dark_effective != dark_effective_previous:
241 | if dark_effective:
242 | data.update(bg_color=THEMES["dark"]["bg_color"])
243 | else:
244 | data.update(bg_color=THEMES["light"]["bg_color"])
245 |
246 | def update_protein_id(value: str):
247 | protein_id.set(value)
248 | color_data = apply_coloring(protein_id.value, color_mode.value)
249 | data.update(color_data=color_data, molecule_id=protein_id.value.lower())
250 |
251 | def update_molecule_key(value: str):
252 | molecule_key.set(value)
253 | data.update(custom_data=molecule_store[molecule_key.value])
254 |
255 | def update_structure_type(value: str):
256 | structure_type.set(value)
257 | if structure_type.value == "Protein":
258 | color_data = apply_coloring(protein_id.value, color_mode.value)
259 | data.update(
260 | color_data=color_data,
261 | custom_data=None,
262 | molecule_id=protein_id.value.lower(),
263 | )
264 | else:
265 | color_data = {"data": [], "nonSelectedColor": None}
266 | data.update(
267 | molecule_id="",
268 | custom_data=molecule_store[molecule_key.value],
269 | color_data=color_data, # used to reset colors
270 | )
271 |
272 | def update_color_mode(value: str):
273 | color_data = apply_coloring(protein_id.value, value)
274 | color_mode.set(value)
275 | data.update(color_data=color_data)
276 |
277 | with solara.AppBar():
278 | solara.lab.ThemeToggle()
279 |
280 | with solara.ColumnsResponsive([4, 8]):
281 | with solara.Card("Controls"):
282 | with solara.ToggleButtonsSingle(
283 | value=structure_type.value,
284 | on_value=update_structure_type,
285 | classes=["d-flex", "flex-row"],
286 | ):
287 | solara.Button(label="Protein", classes=["flex-grow-1"])
288 | solara.Button(label="Molecule", classes=["flex-grow-1"])
289 |
290 | solara.Div(style="height: 20px")
291 |
292 | if structure_type.value == "Protein":
293 | solara.Select(
294 | label="PDB id",
295 | value=protein_id.value,
296 | values=pdb_ids,
297 | on_value=update_protein_id,
298 | )
299 |
300 | solara.Select(
301 | label="Color mode",
302 | value=color_mode.value,
303 | on_value=update_color_mode,
304 | values=color_options,
305 | )
306 | else:
307 | solara.Select(
308 | label="Molecule",
309 | value=molecule_key.value,
310 | values=list(molecule_store.keys()),
311 | on_value=update_molecule_key,
312 | )
313 |
314 | solara.Checkbox(
315 | label="spin",
316 | value=data.value.spin,
317 | on_value=lambda x: data.update(spin=x),
318 | )
319 |
320 | for struc_elem in visibility_cbs:
321 | attr = f"hide_{struc_elem}"
322 |
323 | def on_value(x, attr=attr):
324 | data.update(**{attr: x})
325 |
326 | solara.Checkbox(
327 | label=f"hide {struc_elem}",
328 | value=getattr(data.value, attr),
329 | on_value=on_value,
330 | )
331 |
332 | btn = solara.Button("background color", block=True)
333 | with solara.lab.Menu(activator=btn, close_on_content_click=False):
334 | rv.ColorPicker(
335 | v_model=data.value.bg_color,
336 | on_v_model=lambda x: data.update(bg_color=x),
337 | )
338 |
339 | solara.Div(style="height: 20px")
340 | solara.Button(
341 | "redraw", on_click=lambda: set_counter(counter + 1), block=True
342 | )
343 |
344 | key = f"{counter}_{dark_effective}"
345 | ProteinView(dark_effective).key(key)
346 |
347 |
348 | @solara.component
349 | def Layout(children):
350 | dark_effective = solara.lab.use_dark_effective()
351 | return solara.AppLayout(children=children, toolbar_dark=dark_effective, color=None)
352 |
--------------------------------------------------------------------------------
/examples/pdbemolstar/pyhdx_secb_deltaG.csv:
--------------------------------------------------------------------------------
1 | # PyHDX v0.4.0b2+34.gcb83492
2 | # 2021/08/20 11:32:33 (1629451953)
3 | # {"r1": 2, "epochs": 200000, "patience": 100, "stop_loss": 1e-05, "optimizer": "SGD", "lr": 10000.0, "momentum": 0.5, "nesterov": true, "model_name": "DeltaGFit", "total_loss": 1.72128356163797, "mse_loss": 1.3044053161108615, "reg_loss": 0.4168782455271085, "regularization_percentage": 24.219033680331435, "epochs_run": 66406}
4 | # {"comment": "#", "header": [0], "index_col": 0}
5 | r_number,sequence,_deltaG,deltaG,pfact,k_obs,covariance
6 | 10,T,20919.430436051756,20919.430436051756,4022.3351440972306,2.4547916966364984,2199.361913446584
7 | 11,F,20919.412154717586,20919.412154717586,4022.3059703101985,2.0418216044135877,2271.551332671181
8 | 12,Q,20919.44082172526,20919.44082172526,4022.3517178957886,2.951296593638542,1528.8012869134923
9 | 13,I,21386.831723581752,21386.831723581752,4841.8668761799245,0.5489064390732655,1399.8350238032224
10 | 14,Q,21386.8224886335,21386.8224886335,4841.849136133268,1.2574766119631515,1603.8319233328189
11 | 15,R,21059.19763359226,21059.19763359226,4251.679754775063,4.035879368140441,1865.73481412718
12 | 16,I,21059.164750663786,21059.164750663786,4251.624287549613,0.6545515283426903,1470.3584191504274
13 | 17,Y,21059.170923250273,21059.170923250273,4251.634699469968,0.6697963521460933,1481.3185274088164
14 | 18,T,20693.032146224697,,,,
15 | 19,K,20692.997641104288,20692.997641104288,3676.742619355029,3.540132555080082,1576.3747429038096
16 | 20,D,20693.035167457594,20693.035167457594,3676.7973601459116,4.0657708833246184,1486.0574538989097
17 | 21,I,21360.490530363208,21360.490530363208,4791.529662130848,0.23134588353839522,5986.358829054358
18 | 22,S,35766.64249139626,35766.64249139626,1454439.8024162317,0.008548793110493598,2859.2003704705885
19 | 23,F,37061.97643529757,37061.97643529757,2431563.0143466685,0.004253193573346486,2242.247939793355
20 | 24,E,37061.96631403077,37061.96631403077,2431553.2503466266,0.0033027199089483506,2251.420648907708
21 | 25,A,20813.342791325536,20813.342791325536,3856.5507081213314,1.654236511048838,1607.9204046269394
22 | 26,P,20813.387886890752,,,,
23 | 27,N,18149.998736732974,18149.998736732974,1340.593708979362,11.939326898781227,1414.4500438249765
24 | 28,A,18150.005460981174,18150.005460981174,1340.5972854094032,14.027448586679636,1357.232499614844
25 | 29,P,18534.163621054628,,,,
26 | 30,H,18031.826501806907,18031.826501806907,1279.1920740457272,3.949226556493646,2358.6347356693536
27 | 31,V,34971.949977670265,34971.949977670265,1061128.396762391,0.0026371315086005666,1669.2917177140157
28 | 32,F,29243.181159656666,29243.181159656666,109316.20134441808,0.03434886188829527,357.703018200999
29 | 33,Q,16809.57074962795,16809.57074962795,787.657776938731,15.056092220540023,1556.6671030537907
30 | 34,K,16809.6083372667,16809.6083372667,787.6695230494516,16.508430965688692,2004.5041090215273
31 | 35,D,17254.54663502072,17254.54663502072,939.7410429332765,15.895002704490233,3133.463816091973
32 | 36,W,25415.39983917216,25415.39983917216,23941.246625989035,0.09675238640252719,1070.861350789624
33 | 37,Q,16343.510915312785,16343.510915312785,654.6875927580811,12.243452216995303,3555.338692727679
34 | 38,P,16343.47746288956,,,,
35 | 39,E,16343.537335444113,16343.537335444113,654.6944552145262,6.138386030065746,1362.5326952741855
36 | 40,V,20964.408403054615,20964.408403054615,4094.7565574927858,0.31086734953708856,1398.076164655746
37 | 41,K,15305.883616868221,15305.883616868221,433.75843330767725,13.688404847244868,3012.652445521661
38 | 42,L,15305.819245718982,15305.819245718982,433.7473558042994,7.183960027772581,2293.157055443234
39 | 43,D,14824.745335403722,14824.745335403722,358.3821881515886,19.461403162147157,1620.2126520444083
40 | 44,L,14745.752092739818,14745.752092739818,347.3246870215025,4.496165928583153,1234.0661726400788
41 | 45,D,14745.7480368854,14745.7480368854,347.3241281320621,20.07923393196863,1330.991074925188
42 | 46,T,18420.5268231792,18420.5268231792,1492.484904728409,3.3933272785866087,734.5796499583479
43 | 47,A,37591.36113684838,37591.36113684838,2999853.4654005547,0.004758835257313989,1471.75656217688
44 | 48,S,37591.32862589079,37591.32862589079,2999814.7721474436,0.007038923937679549,1580.8413386090645
45 | 49,S,32028.591208089692,32028.591208089692,330079.630170158,0.12763824282087013,1727.8668697183139
46 | 50,Q,36320.34866276733,36320.34866276733,1811761.0665843543,0.011389369793521746,2305.2280577127667
47 | 51,L,35824.38584117635,35824.38584117635,1488144.4769514224,0.002523222029800536,1612.3035051968263
48 | 52,A,18443.7739792064,18443.7739792064,1506.3139543665934,3.684657598492488,1584.3043893956876
49 | 53,D,17369.504974145206,17369.504974145206,983.5939497352186,11.520558549949104,1894.6929695107722
50 | 54,D,17369.537881463995,17369.537881463995,983.6067913436877,7.615415454300077,4071.652566875675
51 | 55,V,19722.95345724863,19722.95345724863,2502.1988268169202,0.4746038306651238,1460.2188730606017
52 | 56,Y,19722.976953583093,19722.976953583093,2502.222152383326,1.3999143423922167,2641.9472798986594
53 | 57,E,19722.94137510081,19722.94137510081,2502.186832565106,3.1351797277427793,2440.666490250231
54 | 58,V,19817.645823666517,,,,
55 | 59,V,20443.90324338757,,,,
56 | 60,L,21094.215835522362,,,,
57 | 61,R,21744.52842765715,,,,
58 | 62,V,33286.110561087175,33286.110561087175,543616.886313745,0.005486648255651761,2241.0514977604494
59 | 63,T,34262.50322691518,34262.50322691518,800807.650745016,0.006935409363845657,3471.749531744862
60 | 64,V,35183.67277357366,35183.67277357366,1154113.1402045572,0.0024680394334270907,2344.2913945447503
61 | 65,T,36184.558016831186,36184.558016831186,1716737.138673367,0.003235167722968787,1774.2023430999427
62 | 66,A,36184.61155893592,36184.61155893592,1716773.6066860207,0.008315484829028495,3044.303792906657
63 | 67,S,31965.739137051878,31965.739137051878,321950.5199066919,0.06558588402787163,1379.6936295092294
64 | 68,L,28175.665397740027,28175.665397740027,71572.8785865007,0.06604596714994004,1362.1988662523968
65 | 69,G,18049.636343421433,18049.636343421433,1288.2627427779237,4.020308905144217,1540.8920503317502
66 | 70,E,17480.20797232154,17480.20797232154,1027.7566483143535,10.056411895699735,1673.1779467423257
67 | 71,E,17480.18819570885,17480.18819570885,1027.748584347357,4.816758212488473,1879.27845348462
68 | 72,T,18034.703600071225,18034.703600071225,1280.6530618376737,4.237783505289487,1320.4964598132806
69 | 73,A,18034.708121283336,18034.708121283336,1280.6553590175677,11.138574107552488,2055.4342303713393
70 | 74,F,20633.15891003089,20633.15891003089,3590.4827387090454,1.4432018369299298,1595.7249045922301
71 | 75,L,22977.832474821,22977.832474821,9102.210332908211,0.2988176727632178,1424.8094204705324
72 | 76,C,38055.8606966381,38055.8606966381,3606904.6041440717,0.006418980847125078,6187.955049249469
73 | 77,E,38055.86304583672,38055.86304583672,3606907.9658741355,0.006880508355236782,7822.1597818519895
74 | 78,V,35959.84796624756,35959.84796624756,1570310.779939945,0.0008108179545246907,7833.572826668836
75 | 79,Q,36714.51290101607,36714.51290101607,2118441.9060547557,0.0035365849348005223,6155.599716681484
76 | 80,Q,37056.3704517837,37056.3704517837,2426160.913175716,0.0067558667591728055,7083.195225987243
77 | 81,G,37056.415745414335,37056.415745414335,2426204.511407945,0.005491275986559386,12031.078784124582
78 | 82,G,37056.343758330426,37056.343758330426,2426135.219259382,0.005124903298071557,10916.014935637273
79 | 83,I,34189.772843035185,34189.772843035185,778030.3857223899,0.0031886271505492123,5915.103525543223
80 | 84,F,34187.314347557425,34187.314347557425,777271.8737694325,0.003926696587710748,6581.891207965823
81 | 85,S,24398.96224795455,,,,
82 | 86,I,18502.891000433552,18502.891000433552,1542.0609259951618,2.168791399877809,1909.6439837732762
83 | 87,A,17370.72730027929,17370.72730027929,984.0710575144565,5.384350180826554,1611.2677668523293
84 | 88,G,17370.736126437725,17370.736126437725,984.0745034502467,8.533590095632096,2830.6274635774294
85 | 89,I,17370.770853627477,17370.770853627477,984.0880618630993,2.518406319737342,1674.9914512798407
86 | 90,E,17370.772266360487,17370.772266360487,984.0886134353644,4.181002322749086,1471.719415677227
87 | 91,G,17370.777818023937,17370.777818023937,984.0907809700608,6.045510654785301,1744.7874892721293
88 | 92,T,18454.212918661615,18454.212918661615,1512.5653899518584,7.492032766549868,2214.163067154405
89 | 93,Q,18565.580500180018,18565.580500180018,1580.895322819498,10.36151152679036,5802.230049438161
90 | 94,M,18650.68822923237,18650.68822923237,1635.186985747294,8.526443576884947,5016.776144280829
91 | 95,A,20883.27291810903,,,,
92 | 96,H,23907.980116388553,,,,
93 | 97,C,26932.68731466808,,,,
94 | 98,L,29957.394512947605,,,,
95 | 99,G,32157.84975720566,,,,
96 | 100,A,32157.828467489442,32157.828467489442,347445.4885546223,0.038345369726348395,1331.149551118488
97 | 101,Y,33944.833774070474,33944.833774070474,705980.7347118829,0.006851842814223264,2948.4702936197095
98 | 102,C,36724.94262594434,36724.94262594434,2127225.9904860826,0.01980555521932716,1269.2141478508795
99 | 103,P,36810.77452984988,,,,
100 | 104,N,36810.766334191896,36810.766334191896,2200905.031488488,0.007277787251107704,2478.9459565426732
101 | 105,I,34036.66778660016,34036.66778660016,732177.0558956296,0.004786126223067703,2756.184669090075
102 | 106,L,34036.62736599442,34036.62736599442,732165.3143926405,0.0019054217247063178,5400.099638580072
103 | 107,F,34036.66050959576,34036.66050959576,732174.942034909,0.004365011596196762,6830.932109651076
104 | 108,P,34412.55116980607,,,,
105 | 109,Y,35677.89991058932,35677.89991058932,1404122.983391218,0.0019824187606506246,6032.587161999365
106 | 110,A,36736.95068487817,36736.95068487817,2137384.490977934,0.004728441272774879,12586.58616014294
107 | 111,R,37755.1440496577,37755.1440496577,3201255.29505735,0.0033828318504479597,7286.13209998546
108 | 112,E,37755.216431347755,37755.216431347755,3201347.2263797494,0.003625958180277894,7986.939594956758
109 | 113,C,37755.19195063535,37755.19195063535,3201316.133355422,0.008309608189782172,8181.308303959258
110 | 114,I,37219.94106038409,,,,
111 | 115,T,37219.99342127409,37219.99342127409,2588882.332336185,0.0017437666884690065,2231.95118108176
112 | 116,S,39102.217967293866,39102.217967293866,5462922.438286001,0.006125982367458822,1819.6296246184863
113 | 117,M,39102.22333612587,39102.22333612587,5462934.074549683,0.003214954399810702,1756.1083225687696
114 | 118,V,34775.20101549821,34775.20101549821,981448.515948399,0.00235902688976224,1772.9736350929495
115 | 119,S,31712.02125939371,31712.02125939371,291120.51309290633,0.05254441444643108,1442.7855645406123
116 | 120,R,29873.97299648735,29873.97299648735,140401.51249202696,0.15389551982272456,839.440538021873
117 | 121,G,29874.001705835035,29874.001705835035,140403.1117032424,0.09936216144977028,1126.6317611291659
118 | 122,T,29874.057032199416,29874.057032199416,140406.19362688548,0.08076282420378642,1323.39802357682
119 | 123,F,29874.045786474024,29874.045786474024,140405.56718467432,0.058507755129008,1491.7770622412565
120 | 124,P,27180.865410127568,,,,
121 | 125,Q,24940.676339827005,24940.676339827005,19831.270168220122,0.3000740407134874,714.5709876481052
122 | 126,L,24944.253113893177,24944.253113893177,19859.431841919537,0.1890654483688677,831.8619334265086
123 | 127,N,17069.58370709346,17069.58370709346,873.2499079629926,19.632032357449095,1552.620936347317
124 | 128,L,14041.566570657673,14041.566570657673,262.6651498004279,18.77362445992593,1593.1155837640247
125 | 129,A,14041.537793171956,14041.537793171956,262.66215090677565,21.064592684880132,2562.5574829086654
126 | 130,P,13719.961984919912,,,,
127 | 131,V,13107.866705440014,13107.866705440014,181.35238549218082,5.671388620158085,1858.0264623099183
128 | 132,N,16001.759959888463,16001.759959888463,571.6753078967847,35.21213826098592,4731.509974566803
129 | 133,F,16001.735399184396,16001.735399184396,571.6697373686366,18.91022180797328,2402.5108334346082
130 | 134,D,16001.767890625099,,,,
131 | 135,A,16001.753465642932,,,,
132 | 136,L,15445.976927574618,,,,
133 | 137,F,15445.960152271085,,,,
134 | 138,M,15445.920041689666,15445.920041689666,458.53935089828894,21.992679737845545,1659.3476471682227
135 | 139,N,15445.916900977076,15445.916900977076,458.5387795344132,78.03306938267686,34224.96320926817
136 | 140,Y,15445.876530200469,15445.876530200469,458.53143527617556,21.993058571161768,1154.755721966316
137 | 141,L,10004.632777289324,10004.632777289324,52.9452628232315,49.27737252182043,4278.159298908949
138 | 142,Q,10004.66782322247,10004.66782322247,52.94599898964294,118.2065702567813,482392.73647053854
139 | 143,Q,10136.85278903163,10136.85278903163,55.79676219857428,288.58734172713747,293341008925.16125
140 | 144,Q,10136.856729918336,10136.856729918336,55.79684943774523,288.5868984609935,293330599026.5491
141 | 145,A,10136.85624598459,10136.85624598459,55.79683872492582,251.34872850885395,15053511864.55236
142 | 146,G,10058.255380131106,10058.255380131106,54.08370531273599,152.60814388532276,6565657.453759824
143 | 147,E,10058.146362485824,10058.146362485824,54.081366141120284,187.8239651751353,100584866.22360535
144 | 148,G,10058.13118513682,10058.13118513682,54.081040491549984,108.12026713981362,227650.0767580462
145 | 149,T,10058.126693897279,10058.126693897279,54.08094412659722,205.87304149638751,413613238.5413494
146 | 150,E,10058.089738512477,10058.089738512477,54.08015121083325,201.26144389605025,287969589.8329091
147 | 151,E,10058.04630342949,10058.04630342949,54.079219281346546,89.96556699414205,60433.52536198467
148 | 152,H,10058.001570477172,10058.001570477172,54.07825952196978,113.00960682114687,365840.3022100215
149 | 153,Q,10057.987395708305,10057.987395708305,54.077955401457864,292.36294221033376,443907616929.0691
150 | 154,D,10057.946780954537,10057.946780954537,54.07708401896283,326.4072218268354,6816460454893.367
151 | 155,A,4848.998941539874,4848.998941539874,6.8469398956720475,12.026141759383467,1173.980996585565
152 |
--------------------------------------------------------------------------------
/src/ipymolstar/static/pdbe-dark.css:
--------------------------------------------------------------------------------
1 | /**
2 | * pdbe-molstar
3 | * @version 3.3.2
4 | * @link https://github.com/PDBeurope/pdbe-molstar
5 | * @license Apache 2.0
6 | */
7 |
8 | .msp-plugin {
9 | font-family: "Helvetica Neue", "Segoe UI", Helvetica, "Source Sans Pro", Arial, sans-serif;
10 | font-size: 14px;
11 | line-height: 1.42857143;
12 | position: absolute;
13 | left: 0;
14 | top: 0;
15 | right: 0;
16 | bottom: 0;
17 | /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
18 | }
19 |
20 | .msp-plugin * {
21 | box-sizing: border-box
22 | }
23 |
24 | .msp-plugin [hidden],
25 | .msp-plugin template {
26 | display: none
27 | }
28 |
29 | .msp-plugin a {
30 | background-color: rgba(0, 0, 0, 0)
31 | }
32 |
33 | .msp-plugin a:active,
34 | .msp-plugin a:hover {
35 | outline: 0
36 | }
37 |
38 | .msp-plugin abbr[title] {
39 | border-bottom: 1px dotted
40 | }
41 |
42 | .msp-plugin b,
43 | .msp-plugin strong {
44 | font-weight: bold
45 | }
46 |
47 | .msp-plugin small {
48 | font-size: 80%
49 | }
50 |
51 | .msp-plugin img {
52 | border: 0
53 | }
54 |
55 | .msp-plugin svg:not(:root) {
56 | overflow: hidden
57 | }
58 |
59 | .msp-plugin button,
60 | .msp-plugin input,
61 | .msp-plugin optgroup,
62 | .msp-plugin select,
63 | .msp-plugin textarea {
64 | color: inherit;
65 | font: inherit;
66 | margin: 0
67 | }
68 |
69 | .msp-plugin button {
70 | overflow: visible
71 | }
72 |
73 | .msp-plugin button,
74 | .msp-plugin select {
75 | text-transform: none
76 | }
77 |
78 | .msp-plugin button,
79 | .msp-plugin html input[type=button],
80 | .msp-plugin input[type=reset],
81 | .msp-plugin input[type=submit] {
82 | -webkit-appearance: button;
83 | cursor: pointer
84 | }
85 |
86 | .msp-plugin button[disabled],
87 | .msp-plugin html input[disabled] {
88 | cursor: default
89 | }
90 |
91 | .msp-plugin button::-moz-focus-inner,
92 | .msp-plugin input::-moz-focus-inner {
93 | border: 0;
94 | padding: 0
95 | }
96 |
97 | .msp-plugin input {
98 | line-height: normal
99 | }
100 |
101 | .msp-plugin input[type=checkbox],
102 | .msp-plugin input[type=radio] {
103 | box-sizing: border-box;
104 | padding: 0
105 | }
106 |
107 | .msp-plugin input[type=number]::-webkit-inner-spin-button,
108 | .msp-plugin input[type=number]::-webkit-outer-spin-button {
109 | height: auto
110 | }
111 |
112 | .msp-plugin textarea {
113 | overflow: auto
114 | }
115 |
116 | .msp-plugin .msp-layout-expanded,
117 | .msp-plugin .msp-layout-standard {
118 | left: 0;
119 | right: 0;
120 | top: 0;
121 | bottom: 0
122 | }
123 |
124 | .msp-plugin .msp-layout-standard {
125 | border: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
126 | }
127 |
128 | .msp-plugin .msp-layout-region {
129 | overflow: hidden
130 | }
131 |
132 | .msp-plugin .msp-layout-static,
133 | .msp-plugin .msp-layout-scrollable {
134 | position: absolute
135 | }
136 |
137 | .msp-plugin .msp-scrollable {
138 | overflow-y: auto
139 | }
140 |
141 | .msp-plugin .msp-scrollable-container {
142 | position: absolute;
143 | left: 0;
144 | right: 0;
145 | top: 0;
146 | bottom: 0;
147 | overflow-y: auto
148 | }
149 |
150 | .msp-plugin .msp-layout-static {
151 | overflow: hidden
152 | }
153 |
154 | .msp-plugin .msp-layout-top .msp-layout-static,
155 | .msp-plugin .msp-layout-main .msp-layout-static,
156 | .msp-plugin .msp-layout-bottom .msp-layout-static {
157 | left: 0;
158 | right: 0;
159 | top: 0;
160 | bottom: 0
161 | }
162 |
163 | .msp-plugin .msp-layout-right .msp-layout-static {
164 | left: 0;
165 | right: 0;
166 | top: 0;
167 | bottom: 0
168 | }
169 |
170 | .msp-plugin .msp-layout-right .msp-layout-scrollable {
171 | left: 0;
172 | right: 0;
173 | top: 43px;
174 | bottom: 0
175 | }
176 |
177 | .msp-plugin .msp-layout-left .msp-layout-static {
178 | left: 0;
179 | right: 0;
180 | bottom: 0;
181 | top: 0
182 | }
183 |
184 | .msp-plugin .msp-layout-standard-outside {
185 | position: absolute
186 | }
187 |
188 | .msp-plugin .msp-layout-standard-outside .msp-layout-main {
189 | position: absolute;
190 | left: 0;
191 | right: 0;
192 | bottom: 0;
193 | top: 0
194 | }
195 |
196 | .msp-plugin .msp-layout-standard-outside .msp-layout-top {
197 | position: absolute;
198 | right: 0;
199 | height: 97px;
200 | top: -97px;
201 | width: 50%;
202 | border-left: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049);
203 | border-bottom: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
204 | }
205 |
206 | .msp-plugin .msp-layout-standard-outside .msp-layout-bottom {
207 | position: absolute;
208 | left: 0;
209 | right: 0;
210 | height: 97px;
211 | top: -97px;
212 | width: 50%;
213 | border-bottom: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
214 | }
215 |
216 | .msp-plugin .msp-layout-standard-outside .msp-layout-right {
217 | position: absolute;
218 | width: 50%;
219 | right: 0;
220 | bottom: -295px;
221 | height: 295px;
222 | border-left: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049);
223 | border-top: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
224 | }
225 |
226 | .msp-plugin .msp-layout-standard-outside .msp-layout-left {
227 | position: absolute;
228 | width: 50%;
229 | left: 0;
230 | bottom: 0;
231 | bottom: -295px;
232 | height: 295px;
233 | border-top: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
234 | }
235 |
236 | .msp-plugin .msp-layout-standard-outside .msp-layout-hide-right .msp-layout-right {
237 | display: none
238 | }
239 |
240 | .msp-plugin .msp-layout-standard-outside .msp-layout-hide-right .msp-layout-left {
241 | width: 100%
242 | }
243 |
244 | .msp-plugin .msp-layout-standard-outside .msp-layout-hide-left .msp-layout-left {
245 | display: none
246 | }
247 |
248 | .msp-plugin .msp-layout-standard-outside .msp-layout-hide-left .msp-layout-right {
249 | width: 100%;
250 | border-left: none
251 | }
252 |
253 | .msp-plugin .msp-layout-standard-outside .msp-layout-collapse-left .msp-layout-left {
254 | width: 32px
255 | }
256 |
257 | .msp-plugin .msp-layout-standard-outside .msp-layout-collapse-left .msp-layout-right {
258 | left: 32px;
259 | width: auto
260 | }
261 |
262 | .msp-plugin .msp-layout-standard-outside .msp-layout-hide-top .msp-layout-top {
263 | display: none
264 | }
265 |
266 | .msp-plugin .msp-layout-standard-outside .msp-layout-hide-top .msp-layout-bottom {
267 | width: 100%;
268 | border-left: none
269 | }
270 |
271 | .msp-plugin .msp-layout-standard-outside .msp-layout-hide-bottom .msp-layout-bottom {
272 | display: none
273 | }
274 |
275 | .msp-plugin .msp-layout-standard-outside .msp-layout-hide-bottom .msp-layout-top {
276 | width: 100%;
277 | border-left: none
278 | }
279 |
280 | .msp-plugin .msp-layout-standard-landscape {
281 | position: absolute
282 | }
283 |
284 | .msp-plugin .msp-layout-standard-landscape .msp-layout-main {
285 | position: absolute;
286 | left: 330px;
287 | right: 300px;
288 | bottom: 70px;
289 | top: 100px
290 | }
291 |
292 | .msp-plugin .msp-layout-standard-landscape .msp-layout-top {
293 | position: absolute;
294 | left: 330px;
295 | right: 300px;
296 | height: 100px;
297 | top: 0;
298 | border-bottom: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
299 | }
300 |
301 | .msp-plugin .msp-layout-standard-landscape .msp-layout-bottom {
302 | position: absolute;
303 | left: 330px;
304 | right: 300px;
305 | height: 70px;
306 | bottom: 0;
307 | border-top: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
308 | }
309 |
310 | .msp-plugin .msp-layout-standard-landscape .msp-layout-right {
311 | position: absolute;
312 | width: 300px;
313 | right: 0;
314 | bottom: 0;
315 | top: 0;
316 | border-left: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
317 | }
318 |
319 | .msp-plugin .msp-layout-standard-landscape .msp-layout-left {
320 | position: absolute;
321 | width: 330px;
322 | left: 0;
323 | bottom: 0;
324 | top: 0;
325 | border-right: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
326 | }
327 |
328 | .msp-plugin .msp-layout-standard-landscape .msp-layout-hide-right .msp-layout-right {
329 | display: none
330 | }
331 |
332 | .msp-plugin .msp-layout-standard-landscape .msp-layout-hide-right .msp-layout-main,
333 | .msp-plugin .msp-layout-standard-landscape .msp-layout-hide-right .msp-layout-top,
334 | .msp-plugin .msp-layout-standard-landscape .msp-layout-hide-right .msp-layout-bottom {
335 | right: 0
336 | }
337 |
338 | .msp-plugin .msp-layout-standard-landscape .msp-layout-hide-left .msp-layout-left {
339 | display: none
340 | }
341 |
342 | .msp-plugin .msp-layout-standard-landscape .msp-layout-hide-left .msp-layout-main,
343 | .msp-plugin .msp-layout-standard-landscape .msp-layout-hide-left .msp-layout-top,
344 | .msp-plugin .msp-layout-standard-landscape .msp-layout-hide-left .msp-layout-bottom {
345 | left: 0
346 | }
347 |
348 | .msp-plugin .msp-layout-standard-landscape .msp-layout-collapse-left .msp-layout-left {
349 | width: 32px
350 | }
351 |
352 | .msp-plugin .msp-layout-standard-landscape .msp-layout-collapse-left .msp-layout-main,
353 | .msp-plugin .msp-layout-standard-landscape .msp-layout-collapse-left .msp-layout-top,
354 | .msp-plugin .msp-layout-standard-landscape .msp-layout-collapse-left .msp-layout-bottom {
355 | left: 32px
356 | }
357 |
358 | .msp-plugin .msp-layout-standard-landscape .msp-layout-hide-bottom .msp-layout-bottom {
359 | display: none
360 | }
361 |
362 | .msp-plugin .msp-layout-standard-landscape .msp-layout-hide-bottom .msp-layout-main {
363 | bottom: 0
364 | }
365 |
366 | .msp-plugin .msp-layout-standard-landscape .msp-layout-hide-top .msp-layout-top {
367 | display: none
368 | }
369 |
370 | .msp-plugin .msp-layout-standard-landscape .msp-layout-hide-top .msp-layout-main {
371 | top: 0
372 | }
373 |
374 | .msp-plugin .msp-layout-standard-portrait {
375 | position: absolute
376 | }
377 |
378 | .msp-plugin .msp-layout-standard-portrait .msp-layout-main {
379 | position: absolute;
380 | left: 0;
381 | right: 0;
382 | bottom: 361px;
383 | top: 97px
384 | }
385 |
386 | .msp-plugin .msp-layout-standard-portrait .msp-layout-top {
387 | position: absolute;
388 | right: 0;
389 | height: 97px;
390 | top: 0;
391 | width: 50%;
392 | border-left: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049);
393 | border-bottom: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
394 | }
395 |
396 | .msp-plugin .msp-layout-standard-portrait .msp-layout-bottom {
397 | position: absolute;
398 | left: 0;
399 | right: 0;
400 | height: 97px;
401 | width: 50%;
402 | border-bottom: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
403 | }
404 |
405 | .msp-plugin .msp-layout-standard-portrait .msp-layout-right {
406 | position: absolute;
407 | width: 50%;
408 | right: 0;
409 | bottom: 0;
410 | height: 361px;
411 | border-left: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049);
412 | border-top: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
413 | }
414 |
415 | .msp-plugin .msp-layout-standard-portrait .msp-layout-left {
416 | position: absolute;
417 | width: 50%;
418 | left: 0;
419 | bottom: 0;
420 | height: 361px;
421 | border-top: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
422 | }
423 |
424 | .msp-plugin .msp-layout-standard-portrait .msp-layout-hide-right .msp-layout-right {
425 | display: none
426 | }
427 |
428 | .msp-plugin .msp-layout-standard-portrait .msp-layout-hide-right .msp-layout-left {
429 | width: 100%
430 | }
431 |
432 | .msp-plugin .msp-layout-standard-portrait .msp-layout-hide-left .msp-layout-left {
433 | display: none
434 | }
435 |
436 | .msp-plugin .msp-layout-standard-portrait .msp-layout-hide-left .msp-layout-right {
437 | width: 100%;
438 | border-left: none
439 | }
440 |
441 | .msp-plugin .msp-layout-standard-portrait .msp-layout-hide-right.msp-layout-hide-left .msp-layout-main {
442 | bottom: 0
443 | }
444 |
445 | .msp-plugin .msp-layout-standard-portrait .msp-layout-collapse-left .msp-layout-left {
446 | width: 32px
447 | }
448 |
449 | .msp-plugin .msp-layout-standard-portrait .msp-layout-collapse-left .msp-layout-right {
450 | left: 32px;
451 | width: auto
452 | }
453 |
454 | .msp-plugin .msp-layout-standard-portrait .msp-layout-hide-top .msp-layout-top {
455 | display: none
456 | }
457 |
458 | .msp-plugin .msp-layout-standard-portrait .msp-layout-hide-top .msp-layout-bottom {
459 | width: 100%;
460 | border-left: none
461 | }
462 |
463 | .msp-plugin .msp-layout-standard-portrait .msp-layout-hide-bottom .msp-layout-bottom {
464 | display: none
465 | }
466 |
467 | .msp-plugin .msp-layout-standard-portrait .msp-layout-hide-bottom .msp-layout-top {
468 | width: 100%;
469 | border-left: none
470 | }
471 |
472 | .msp-plugin .msp-layout-standard-portrait .msp-layout-hide-top.msp-layout-hide-bottom .msp-layout-main {
473 | top: 0
474 | }
475 |
476 | .msp-plugin .msp-layout-standard-reactive {
477 | position: absolute
478 | }
479 |
480 | @media(orientation: landscape), (min-width: 1000px) {
481 | .msp-plugin .msp-layout-standard-reactive .msp-layout-main {
482 | position: absolute;
483 | left: 330px;
484 | right: 300px;
485 | bottom: 70px;
486 | top: 100px
487 | }
488 |
489 | .msp-plugin .msp-layout-standard-reactive .msp-layout-top {
490 | position: absolute;
491 | left: 330px;
492 | right: 300px;
493 | height: 100px;
494 | top: 0;
495 | border-bottom: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
496 | }
497 |
498 | .msp-plugin .msp-layout-standard-reactive .msp-layout-bottom {
499 | position: absolute;
500 | left: 330px;
501 | right: 300px;
502 | height: 70px;
503 | bottom: 0;
504 | border-top: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
505 | }
506 |
507 | .msp-plugin .msp-layout-standard-reactive .msp-layout-right {
508 | position: absolute;
509 | width: 300px;
510 | right: 0;
511 | bottom: 0;
512 | top: 0;
513 | border-left: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
514 | }
515 |
516 | .msp-plugin .msp-layout-standard-reactive .msp-layout-left {
517 | position: absolute;
518 | width: 330px;
519 | left: 0;
520 | bottom: 0;
521 | top: 0;
522 | border-right: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
523 | }
524 |
525 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-right .msp-layout-right {
526 | display: none
527 | }
528 |
529 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-right .msp-layout-main,
530 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-right .msp-layout-top,
531 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-right .msp-layout-bottom {
532 | right: 0
533 | }
534 |
535 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-left .msp-layout-left {
536 | display: none
537 | }
538 |
539 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-left .msp-layout-main,
540 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-left .msp-layout-top,
541 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-left .msp-layout-bottom {
542 | left: 0
543 | }
544 |
545 | .msp-plugin .msp-layout-standard-reactive .msp-layout-collapse-left .msp-layout-left {
546 | width: 32px
547 | }
548 |
549 | .msp-plugin .msp-layout-standard-reactive .msp-layout-collapse-left .msp-layout-main,
550 | .msp-plugin .msp-layout-standard-reactive .msp-layout-collapse-left .msp-layout-top,
551 | .msp-plugin .msp-layout-standard-reactive .msp-layout-collapse-left .msp-layout-bottom {
552 | left: 32px
553 | }
554 |
555 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-bottom .msp-layout-bottom {
556 | display: none
557 | }
558 |
559 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-bottom .msp-layout-main {
560 | bottom: 0
561 | }
562 |
563 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-top .msp-layout-top {
564 | display: none
565 | }
566 |
567 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-top .msp-layout-main {
568 | top: 0
569 | }
570 | }
571 |
572 | @media(orientation: portrait)and (max-width: 1000px) {
573 | .msp-plugin .msp-layout-standard-reactive .msp-layout-main {
574 | position: absolute;
575 | left: 0;
576 | right: 0;
577 | bottom: 361px;
578 | top: 97px
579 | }
580 |
581 | .msp-plugin .msp-layout-standard-reactive .msp-layout-top {
582 | position: absolute;
583 | right: 0;
584 | height: 97px;
585 | top: 0;
586 | width: 50%;
587 | border-left: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049);
588 | border-bottom: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
589 | }
590 |
591 | .msp-plugin .msp-layout-standard-reactive .msp-layout-bottom {
592 | position: absolute;
593 | left: 0;
594 | right: 0;
595 | height: 97px;
596 | width: 50%;
597 | border-bottom: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
598 | }
599 |
600 | .msp-plugin .msp-layout-standard-reactive .msp-layout-right {
601 | position: absolute;
602 | width: 50%;
603 | right: 0;
604 | bottom: 0;
605 | height: 361px;
606 | border-left: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049);
607 | border-top: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
608 | }
609 |
610 | .msp-plugin .msp-layout-standard-reactive .msp-layout-left {
611 | position: absolute;
612 | width: 50%;
613 | left: 0;
614 | bottom: 0;
615 | height: 361px;
616 | border-top: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
617 | }
618 |
619 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-right .msp-layout-right {
620 | display: none
621 | }
622 |
623 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-right .msp-layout-left {
624 | width: 100%
625 | }
626 |
627 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-left .msp-layout-left {
628 | display: none
629 | }
630 |
631 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-left .msp-layout-right {
632 | width: 100%;
633 | border-left: none
634 | }
635 |
636 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-right.msp-layout-hide-left .msp-layout-main {
637 | bottom: 0
638 | }
639 |
640 | .msp-plugin .msp-layout-standard-reactive .msp-layout-collapse-left .msp-layout-left {
641 | width: 32px
642 | }
643 |
644 | .msp-plugin .msp-layout-standard-reactive .msp-layout-collapse-left .msp-layout-right {
645 | left: 32px;
646 | width: auto
647 | }
648 |
649 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-top .msp-layout-top {
650 | display: none
651 | }
652 |
653 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-top .msp-layout-bottom {
654 | width: 100%;
655 | border-left: none
656 | }
657 |
658 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-bottom .msp-layout-bottom {
659 | display: none
660 | }
661 |
662 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-bottom .msp-layout-top {
663 | width: 100%;
664 | border-left: none
665 | }
666 |
667 | .msp-plugin .msp-layout-standard-reactive .msp-layout-hide-top.msp-layout-hide-bottom .msp-layout-main {
668 | top: 0
669 | }
670 | }
671 |
672 | .msp-plugin .msp-layout-expanded {
673 | position: fixed
674 | }
675 |
676 | @media(orientation: landscape) {
677 | .msp-plugin .msp-layout-expanded .msp-layout-main {
678 | position: absolute;
679 | left: 330px;
680 | right: 300px;
681 | bottom: 70px;
682 | top: 100px
683 | }
684 |
685 | .msp-plugin .msp-layout-expanded .msp-layout-top {
686 | position: absolute;
687 | left: 330px;
688 | right: 300px;
689 | height: 100px;
690 | top: 0;
691 | border-bottom: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
692 | }
693 |
694 | .msp-plugin .msp-layout-expanded .msp-layout-bottom {
695 | position: absolute;
696 | left: 330px;
697 | right: 300px;
698 | height: 70px;
699 | bottom: 0;
700 | border-top: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
701 | }
702 |
703 | .msp-plugin .msp-layout-expanded .msp-layout-right {
704 | position: absolute;
705 | width: 300px;
706 | right: 0;
707 | bottom: 0;
708 | top: 0;
709 | border-left: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
710 | }
711 |
712 | .msp-plugin .msp-layout-expanded .msp-layout-left {
713 | position: absolute;
714 | width: 330px;
715 | left: 0;
716 | bottom: 0;
717 | top: 0;
718 | border-right: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
719 | }
720 |
721 | .msp-plugin .msp-layout-expanded .msp-layout-hide-right .msp-layout-right {
722 | display: none
723 | }
724 |
725 | .msp-plugin .msp-layout-expanded .msp-layout-hide-right .msp-layout-main,
726 | .msp-plugin .msp-layout-expanded .msp-layout-hide-right .msp-layout-top,
727 | .msp-plugin .msp-layout-expanded .msp-layout-hide-right .msp-layout-bottom {
728 | right: 0
729 | }
730 |
731 | .msp-plugin .msp-layout-expanded .msp-layout-hide-left .msp-layout-left {
732 | display: none
733 | }
734 |
735 | .msp-plugin .msp-layout-expanded .msp-layout-hide-left .msp-layout-main,
736 | .msp-plugin .msp-layout-expanded .msp-layout-hide-left .msp-layout-top,
737 | .msp-plugin .msp-layout-expanded .msp-layout-hide-left .msp-layout-bottom {
738 | left: 0
739 | }
740 |
741 | .msp-plugin .msp-layout-expanded .msp-layout-collapse-left .msp-layout-left {
742 | width: 32px
743 | }
744 |
745 | .msp-plugin .msp-layout-expanded .msp-layout-collapse-left .msp-layout-main,
746 | .msp-plugin .msp-layout-expanded .msp-layout-collapse-left .msp-layout-top,
747 | .msp-plugin .msp-layout-expanded .msp-layout-collapse-left .msp-layout-bottom {
748 | left: 32px
749 | }
750 |
751 | .msp-plugin .msp-layout-expanded .msp-layout-hide-bottom .msp-layout-bottom {
752 | display: none
753 | }
754 |
755 | .msp-plugin .msp-layout-expanded .msp-layout-hide-bottom .msp-layout-main {
756 | bottom: 0
757 | }
758 |
759 | .msp-plugin .msp-layout-expanded .msp-layout-hide-top .msp-layout-top {
760 | display: none
761 | }
762 |
763 | .msp-plugin .msp-layout-expanded .msp-layout-hide-top .msp-layout-main {
764 | top: 0
765 | }
766 | }
767 |
768 | @media(orientation: portrait) {
769 | .msp-plugin .msp-layout-expanded .msp-layout-main {
770 | position: absolute;
771 | left: 0;
772 | right: 0;
773 | bottom: 361px;
774 | top: 97px
775 | }
776 |
777 | .msp-plugin .msp-layout-expanded .msp-layout-top {
778 | position: absolute;
779 | right: 0;
780 | height: 97px;
781 | top: 0;
782 | width: 50%;
783 | border-left: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049);
784 | border-bottom: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
785 | }
786 |
787 | .msp-plugin .msp-layout-expanded .msp-layout-bottom {
788 | position: absolute;
789 | left: 0;
790 | right: 0;
791 | height: 97px;
792 | width: 50%;
793 | border-bottom: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
794 | }
795 |
796 | .msp-plugin .msp-layout-expanded .msp-layout-right {
797 | position: absolute;
798 | width: 50%;
799 | right: 0;
800 | bottom: 0;
801 | height: 361px;
802 | border-left: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049);
803 | border-top: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
804 | }
805 |
806 | .msp-plugin .msp-layout-expanded .msp-layout-left {
807 | position: absolute;
808 | width: 50%;
809 | left: 0;
810 | bottom: 0;
811 | height: 361px;
812 | border-top: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
813 | }
814 |
815 | .msp-plugin .msp-layout-expanded .msp-layout-hide-right .msp-layout-right {
816 | display: none
817 | }
818 |
819 | .msp-plugin .msp-layout-expanded .msp-layout-hide-right .msp-layout-left {
820 | width: 100%
821 | }
822 |
823 | .msp-plugin .msp-layout-expanded .msp-layout-hide-left .msp-layout-left {
824 | display: none
825 | }
826 |
827 | .msp-plugin .msp-layout-expanded .msp-layout-hide-left .msp-layout-right {
828 | width: 100%;
829 | border-left: none
830 | }
831 |
832 | .msp-plugin .msp-layout-expanded .msp-layout-hide-right.msp-layout-hide-left .msp-layout-main {
833 | bottom: 0
834 | }
835 |
836 | .msp-plugin .msp-layout-expanded .msp-layout-collapse-left .msp-layout-left {
837 | width: 32px
838 | }
839 |
840 | .msp-plugin .msp-layout-expanded .msp-layout-collapse-left .msp-layout-right {
841 | left: 32px;
842 | width: auto
843 | }
844 |
845 | .msp-plugin .msp-layout-expanded .msp-layout-hide-top .msp-layout-top {
846 | display: none
847 | }
848 |
849 | .msp-plugin .msp-layout-expanded .msp-layout-hide-top .msp-layout-bottom {
850 | width: 100%;
851 | border-left: none
852 | }
853 |
854 | .msp-plugin .msp-layout-expanded .msp-layout-hide-bottom .msp-layout-bottom {
855 | display: none
856 | }
857 |
858 | .msp-plugin .msp-layout-expanded .msp-layout-hide-bottom .msp-layout-top {
859 | width: 100%;
860 | border-left: none
861 | }
862 |
863 | .msp-plugin .msp-layout-expanded .msp-layout-hide-top.msp-layout-hide-bottom .msp-layout-main {
864 | top: 0
865 | }
866 | }
867 |
868 | .msp-plugin ::-webkit-scrollbar {
869 | width: 10px;
870 | height: 10px
871 | }
872 |
873 | .msp-plugin ::-webkit-scrollbar-track {
874 | border-radius: 0;
875 | background-color: rgb(22.2865853659, 24.9085365854, 31.4634146341)
876 | }
877 |
878 | .msp-plugin ::-webkit-scrollbar-thumb {
879 | border-radius: 0;
880 | background-color: rgb(13.8280487805, 15.4548780488, 19.5219512195)
881 | }
882 |
883 | .msp-plugin .msp-form-control,
884 | .msp-plugin .msp-control-row select,
885 | .msp-plugin .msp-control-row button,
886 | .msp-plugin .msp-control-row input[type=text],
887 | .msp-plugin .msp-btn {
888 | display: block;
889 | width: 100%;
890 | background: rgb(11.7134146341, 13.0914634146, 16.5365853659);
891 | border: none;
892 | padding: 0 10px;
893 | line-height: 30px;
894 | height: 32px;
895 | -webkit-appearance: none;
896 | -moz-appearance: none;
897 | appearance: none;
898 | -webkit-box-shadow: none;
899 | box-shadow: none;
900 | background-image: none
901 | }
902 |
903 | .msp-plugin .msp-form-control::-moz-placeholder,
904 | .msp-plugin .msp-control-row select::-moz-placeholder,
905 | .msp-plugin .msp-control-row button::-moz-placeholder,
906 | .msp-plugin .msp-control-row input[type=text]::-moz-placeholder,
907 | .msp-plugin .msp-btn::-moz-placeholder {
908 | color: hsl(216, 24.3902439024%, 50.9215686275%);
909 | opacity: 1
910 | }
911 |
912 | .msp-plugin .msp-form-control:-ms-input-placeholder,
913 | .msp-plugin .msp-control-row select:-ms-input-placeholder,
914 | .msp-plugin .msp-control-row button:-ms-input-placeholder,
915 | .msp-plugin .msp-control-row input[type=text]:-ms-input-placeholder,
916 | .msp-plugin .msp-btn:-ms-input-placeholder {
917 | color: hsl(216, 24.3902439024%, 50.9215686275%)
918 | }
919 |
920 | .msp-plugin .msp-form-control::-webkit-input-placeholder,
921 | .msp-plugin .msp-control-row select::-webkit-input-placeholder,
922 | .msp-plugin .msp-control-row button::-webkit-input-placeholder,
923 | .msp-plugin .msp-control-row input[type=text]::-webkit-input-placeholder,
924 | .msp-plugin .msp-btn::-webkit-input-placeholder {
925 | color: hsl(216, 24.3902439024%, 50.9215686275%)
926 | }
927 |
928 | .msp-plugin .msp-form-control:hover,
929 | .msp-plugin .msp-control-row select:hover,
930 | .msp-plugin .msp-control-row button:hover,
931 | .msp-plugin .msp-control-row input[type=text]:hover,
932 | .msp-plugin .msp-btn:hover {
933 | color: #51a2fb;
934 | background-color: rgb(22.2865853659, 24.9085365854, 31.4634146341);
935 | border: none;
936 | outline-offset: -1px !important;
937 | outline: 1px solid rgb(54.006097561, 60.3597560976, 76.243902439) !important
938 | }
939 |
940 | .msp-plugin .msp-form-control:active,
941 | .msp-plugin .msp-control-row select:active,
942 | .msp-plugin .msp-control-row button:active,
943 | .msp-plugin .msp-control-row input[type=text]:active,
944 | .msp-plugin .msp-btn:active,
945 | .msp-plugin .msp-form-control:focus,
946 | .msp-plugin .msp-control-row select:focus,
947 | .msp-plugin .msp-control-row button:focus,
948 | .msp-plugin .msp-control-row input[type=text]:focus,
949 | .msp-plugin .msp-btn:focus {
950 | color: #ccd4e0;
951 | background-color: rgb(11.7134146341, 13.0914634146, 16.5365853659);
952 | border: none;
953 | outline-offset: 0;
954 | outline: none
955 | }
956 |
957 | .msp-plugin .msp-form-control[disabled],
958 | .msp-plugin .msp-control-row select[disabled],
959 | .msp-plugin .msp-control-row button[disabled],
960 | .msp-plugin .msp-control-row input[disabled][type=text],
961 | .msp-plugin [disabled].msp-btn,
962 | .msp-plugin .msp-form-control[readonly],
963 | .msp-plugin .msp-control-row select[readonly],
964 | .msp-plugin .msp-control-row button[readonly],
965 | .msp-plugin .msp-control-row input[readonly][type=text],
966 | .msp-plugin [readonly].msp-btn,
967 | fieldset[disabled] .msp-plugin .msp-form-control,
968 | fieldset[disabled] .msp-plugin .msp-control-row select,
969 | fieldset[disabled] .msp-plugin .msp-control-row button,
970 | fieldset[disabled] .msp-plugin .msp-control-row input[type=text],
971 | fieldset[disabled] .msp-plugin .msp-btn {
972 | background: #111318;
973 | opacity: .35
974 | }
975 |
976 | .msp-plugin .msp-btn,
977 | .msp-plugin .msp-control-row button {
978 | display: inline-block;
979 | margin-bottom: 0;
980 | text-align: center;
981 | touch-action: manipulation;
982 | cursor: pointer;
983 | background-image: none;
984 | white-space: nowrap;
985 | -webkit-user-select: none;
986 | -moz-user-select: none;
987 | -ms-user-select: none;
988 | user-select: none;
989 | padding: 0 10px;
990 | line-height: 32px;
991 | border: none;
992 | -moz-box-sizing: border-box;
993 | box-sizing: border-box
994 | }
995 |
996 | .msp-plugin .msp-btn[disabled],
997 | .msp-plugin .msp-control-row button[disabled] {
998 | background: #111318;
999 | opacity: .35
1000 | }
1001 |
1002 | .msp-plugin .msp-btn-block,
1003 | .msp-plugin .msp-control-row button {
1004 | display: block;
1005 | width: 100%
1006 | }
1007 |
1008 | .msp-plugin .msp-btn,
1009 | .msp-plugin .msp-control-row button,
1010 | .msp-plugin .msp-btn:active,
1011 | .msp-plugin .msp-btn-link:focus,
1012 | .msp-plugin .msp-btn:hover {
1013 | outline: none
1014 | }
1015 |
1016 | .msp-plugin .msp-material-icon svg {
1017 | display: inline-flex;
1018 | vertical-align: middle;
1019 | font-size: 1.2em;
1020 | margin-bottom: 3px;
1021 | fill: currentColor;
1022 | width: 1em;
1023 | height: 1em;
1024 | flex-shrink: 0;
1025 | user-select: none
1026 | }
1027 |
1028 | .msp-plugin .msp-btn-block>.msp-material-icon,
1029 | .msp-plugin .msp-control-row button>.msp-material-icon {
1030 | margin-left: 0;
1031 | margin-right: .4em
1032 | }
1033 |
1034 | .msp-plugin .msp-btn-childless>.msp-material-icon {
1035 | margin-left: 0;
1036 | margin-right: 0
1037 | }
1038 |
1039 | .msp-plugin .msp-btn-icon {
1040 | border: none;
1041 | height: 32px;
1042 | width: 32px;
1043 | line-height: 32px;
1044 | padding: 0;
1045 | text-align: center
1046 | }
1047 |
1048 | .msp-plugin .msp-btn-icon:hover {
1049 | color: #51a2fb;
1050 | background-color: rgb(22.2865853659, 24.9085365854, 31.4634146341);
1051 | border: none;
1052 | outline-offset: -1px !important;
1053 | outline: 1px solid rgb(54.006097561, 60.3597560976, 76.243902439) !important
1054 | }
1055 |
1056 | .msp-plugin .msp-btn-icon[disabled],
1057 | .msp-plugin .msp-btn-icon[disabled]:hover,
1058 | .msp-plugin .msp-btn-icon[disabled]:active {
1059 | color: hsl(216, 24.3902439024%, 50.9215686275%)
1060 | }
1061 |
1062 | .msp-plugin .msp-btn-icon-small {
1063 | border: none;
1064 | height: 32px;
1065 | width: 20px;
1066 | line-height: 32px;
1067 | padding: 0;
1068 | text-align: center
1069 | }
1070 |
1071 | .msp-plugin .msp-btn-icon-small:hover {
1072 | color: #51a2fb;
1073 | background-color: rgb(22.2865853659, 24.9085365854, 31.4634146341);
1074 | border: none;
1075 | outline-offset: -1px !important;
1076 | outline: 1px solid rgb(54.006097561, 60.3597560976, 76.243902439) !important
1077 | }
1078 |
1079 | .msp-plugin .msp-btn-icon-small[disabled],
1080 | .msp-plugin .msp-btn-icon-small[disabled]:hover,
1081 | .msp-plugin .msp-btn-icon-small[disabled]:active {
1082 | color: hsl(216, 24.3902439024%, 50.9215686275%)
1083 | }
1084 |
1085 | .msp-plugin .msp-btn-link {
1086 | font-weight: normal;
1087 | border-radius: 0
1088 | }
1089 |
1090 | .msp-plugin .msp-btn-link,
1091 | .msp-plugin .msp-btn-link:active,
1092 | .msp-plugin .msp-btn-link.active,
1093 | .msp-plugin .msp-btn-link[disabled],
1094 | fieldset[disabled] .msp-plugin .msp-btn-link {
1095 | background-color: rgba(0, 0, 0, 0);
1096 | -webkit-box-shadow: none;
1097 | box-shadow: none
1098 | }
1099 |
1100 | .msp-plugin .msp-btn-link,
1101 | .msp-plugin .msp-btn-link:hover,
1102 | .msp-plugin .msp-btn-link:focus,
1103 | .msp-plugin .msp-btn-link:active {
1104 | border-color: rgba(0, 0, 0, 0)
1105 | }
1106 |
1107 | .msp-plugin .msp-btn-link:hover,
1108 | .msp-plugin .msp-btn-link:focus {
1109 | text-decoration: none;
1110 | background-color: rgba(0, 0, 0, 0)
1111 | }
1112 |
1113 | .msp-plugin .msp-btn-link[disabled]:hover,
1114 | .msp-plugin .msp-btn-link[disabled]:focus,
1115 | fieldset[disabled] .msp-plugin .msp-btn-link:hover,
1116 | fieldset[disabled] .msp-plugin .msp-btn-link:focus {
1117 | text-decoration: none
1118 | }
1119 |
1120 | .msp-plugin .msp-btn-link .msp-icon {
1121 | font-size: 100%
1122 | }
1123 |
1124 | .msp-plugin .msp-btn-link,
1125 | .msp-plugin .msp-btn-link:active,
1126 | .msp-plugin .msp-btn-link:focus {
1127 | color: #ccd4e0;
1128 | text-decoration: none
1129 | }
1130 |
1131 | .msp-plugin .msp-btn-link:hover {
1132 | color: #51a2fb;
1133 | text-decoration: none
1134 | }
1135 |
1136 | .msp-plugin .msp-btn-link-toggle-on {
1137 | color: #ccd4e0
1138 | }
1139 |
1140 | .msp-plugin .msp-btn-link-toggle-off,
1141 | .msp-plugin .msp-btn-link-toggle-off:active,
1142 | .msp-plugin .msp-btn-link-toggle-off:focus {
1143 | color: hsl(216, 24.3902439024%, 50.9215686275%) !important
1144 | }
1145 |
1146 | .msp-plugin .msp-btn-link-toggle-off:hover,
1147 | .msp-plugin .msp-btn-link-toggle-on:hover {
1148 | color: #51a2fb !important
1149 | }
1150 |
1151 | .msp-plugin .msp-btn-action,
1152 | .msp-plugin .msp-btn-action:active,
1153 | .msp-plugin .msp-btn-action:focus {
1154 | color: #ccd4e0;
1155 | background: rgb(11.7134146341, 13.0914634146, 16.5365853659)
1156 | }
1157 |
1158 | .msp-plugin .msp-btn-action:hover {
1159 | color: #51a2fb;
1160 | background: rgb(6.4268292683, 7.1829268293, 9.0731707317)
1161 | }
1162 |
1163 | .msp-plugin .msp-btn-action[disabled],
1164 | .msp-plugin .msp-btn-action[disabled]:hover,
1165 | .msp-plugin .msp-btn-action[disabled]:active,
1166 | .msp-plugin .msp-btn-action[disabled]:focus {
1167 | color: hsl(216, 24.3902439024%, 82.9215686275%)
1168 | }
1169 |
1170 | .msp-plugin .msp-btn-commit-on,
1171 | .msp-plugin .msp-btn-commit-on:active,
1172 | .msp-plugin .msp-btn-commit-on:focus {
1173 | color: #68befd;
1174 | background: rgb(12.7707317073, 14.2731707317, 18.0292682927)
1175 | }
1176 |
1177 | .msp-plugin .msp-btn-commit-on:hover {
1178 | color: #51a2fb;
1179 | background: rgb(7.4841463415, 8.3646341463, 10.5658536585)
1180 | }
1181 |
1182 | .msp-plugin .msp-btn-commit-on[disabled],
1183 | .msp-plugin .msp-btn-commit-on[disabled]:hover,
1184 | .msp-plugin .msp-btn-commit-on[disabled]:active,
1185 | .msp-plugin .msp-btn-commit-on[disabled]:focus {
1186 | color: hsl(205.3691275168, 97.385620915%, 69%)
1187 | }
1188 |
1189 | .msp-plugin .msp-btn-commit-off,
1190 | .msp-plugin .msp-btn-commit-off:active,
1191 | .msp-plugin .msp-btn-commit-off:focus {
1192 | color: #ccd4e0;
1193 | background: rgb(8.5414634146, 9.5463414634, 12.0585365854)
1194 | }
1195 |
1196 | .msp-plugin .msp-btn-commit-off:hover {
1197 | color: #51a2fb;
1198 | background: rgb(3.2548780488, 3.637804878, 4.5951219512)
1199 | }
1200 |
1201 | .msp-plugin .msp-btn-commit-off[disabled],
1202 | .msp-plugin .msp-btn-commit-off[disabled]:hover,
1203 | .msp-plugin .msp-btn-commit-off[disabled]:active,
1204 | .msp-plugin .msp-btn-commit-off[disabled]:focus {
1205 | color: hsl(216, 24.3902439024%, 82.9215686275%)
1206 | }
1207 |
1208 | .msp-plugin .msp-btn-remove:hover {
1209 | color: #f2f4f7
1210 | }
1211 |
1212 | .msp-plugin .msp-btn-commit-on:hover {
1213 | color: hsl(205.3691275168, 97.385620915%, 50%)
1214 | }
1215 |
1216 | .msp-plugin .msp-btn-action {
1217 | height: 32px;
1218 | line-height: 32px
1219 | }
1220 |
1221 | .msp-plugin input[type=file] {
1222 | display: block
1223 | }
1224 |
1225 | .msp-plugin input[type=range] {
1226 | display: block;
1227 | width: 100%
1228 | }
1229 |
1230 | .msp-plugin select[multiple],
1231 | .msp-plugin select[size] {
1232 | height: auto
1233 | }
1234 |
1235 | .msp-plugin textarea.msp-form-control,
1236 | .msp-plugin textarea.msp-btn {
1237 | height: auto
1238 | }
1239 |
1240 | .msp-plugin .msp-control-top-offset {
1241 | margin-top: 1px
1242 | }
1243 |
1244 | .msp-plugin .msp-btn-commit {
1245 | text-align: right;
1246 | padding-top: 0;
1247 | padding-bottom: 0;
1248 | padding-right: 10px;
1249 | padding-left: 0;
1250 | line-height: 32px;
1251 | border: none;
1252 | overflow: hidden;
1253 | font-weight: bold
1254 | }
1255 |
1256 | .msp-plugin .msp-btn-commit .msp-icon {
1257 | display: block-inline;
1258 | line-height: 32px;
1259 | width: 32px;
1260 | text-align: center
1261 | }
1262 |
1263 | .msp-plugin select.msp-form-control,
1264 | .msp-plugin .msp-control-row select,
1265 | .msp-plugin select.msp-btn {
1266 | background: none;
1267 | background-color: rgb(11.7134146341, 13.0914634146, 16.5365853659);
1268 | background-size: 8px 12px;
1269 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAUCAMAAACzvE1FAAAADFBMVEUzMzMzMzMzMzMzMzMKAG/3AAAAA3RSTlMAf4C/aSLHAAAAPElEQVR42q3NMQ4AIAgEQTn//2cLdRKppSGzBYwzVXvznNWs8C58CiussPJj8h6NwgorrKRdTvuV9v16Afn0AYFOB7aYAAAAAElFTkSuQmCC);
1270 | background-repeat: no-repeat;
1271 | background-position: right 10px center;
1272 | padding-right: 24px
1273 | }
1274 |
1275 | .msp-plugin select.msp-form-control:-moz-focusring,
1276 | .msp-plugin .msp-control-row select:-moz-focusring,
1277 | .msp-plugin select.msp-btn:-moz-focusring {
1278 | color: rgba(0, 0, 0, 0);
1279 | text-shadow: 0 0 0 #ccd4e0
1280 | }
1281 |
1282 | .msp-plugin .msp-default-bg {
1283 | background: #111318
1284 | }
1285 |
1286 | .msp-plugin .msp-transparent-bg {
1287 | background: rgba(0, 0, 0, 0)
1288 | }
1289 |
1290 | .msp-plugin .msp-no-hover-outline:hover {
1291 | color: #51a2fb;
1292 | background-color: inherit;
1293 | border: none;
1294 | outline-offset: 0 !important;
1295 | outline: none !important
1296 | }
1297 |
1298 | .msp-plugin .msp-icon-inline {
1299 | margin-right: 8px
1300 | }
1301 |
1302 | .msp-plugin .msp-control-row {
1303 | position: relative;
1304 | height: 32px;
1305 | background: #111318;
1306 | margin-top: 1px
1307 | }
1308 |
1309 | .msp-plugin .msp-control-row>span.msp-control-row-label,
1310 | .msp-plugin .msp-control-row>button.msp-control-button-label {
1311 | line-height: 32px;
1312 | display: block;
1313 | width: 120px;
1314 | text-align: right;
1315 | padding: 0 10px;
1316 | color: hsl(216, 24.3902439024%, 68.9215686275%);
1317 | overflow: hidden;
1318 | text-overflow: ellipsis;
1319 | white-space: nowrap;
1320 | position: relative;
1321 | -webkit-user-select: none;
1322 | -moz-user-select: none;
1323 | -ms-user-select: none;
1324 | -o-user-select: none;
1325 | user-select: none;
1326 | cursor: default
1327 | }
1328 |
1329 | .msp-plugin .msp-control-row>button.msp-control-button-label {
1330 | background: #111318;
1331 | cursor: pointer
1332 | }
1333 |
1334 | .msp-plugin .msp-control-row .msp-control-current {
1335 | background: #111318
1336 | }
1337 |
1338 | .msp-plugin .msp-control-row>div.msp-control-row-ctrl {
1339 | position: absolute;
1340 | left: 120px;
1341 | top: 0;
1342 | right: 0;
1343 | bottom: 0
1344 | }
1345 |
1346 | .msp-plugin .msp-control-row>div {
1347 | background: rgb(11.7134146341, 13.0914634146, 16.5365853659)
1348 | }
1349 |
1350 | .msp-plugin .msp-control-row>.msp-flex-row,
1351 | .msp-plugin .msp-control-row>.msp-state-image-row {
1352 | background: #111318
1353 | }
1354 |
1355 | .msp-plugin .msp-control-label-short>span {
1356 | width: 80px !important
1357 | }
1358 |
1359 | .msp-plugin .msp-control-label-short>div:nth-child(2) {
1360 | left: 80px !important
1361 | }
1362 |
1363 | .msp-plugin .msp-control-col-2 {
1364 | float: left;
1365 | width: 50%
1366 | }
1367 |
1368 | .msp-plugin .msp-control-twoline {
1369 | height: 64px !important
1370 | }
1371 |
1372 | .msp-plugin .msp-control-group {
1373 | position: relative
1374 | }
1375 |
1376 | .msp-plugin .msp-toggle-button .msp-icon {
1377 | display: inline-block;
1378 | margin-right: 6px
1379 | }
1380 |
1381 | .msp-plugin .msp-toggle-button>div>button:hover {
1382 | border-color: rgb(22.2865853659, 24.9085365854, 31.4634146341) !important;
1383 | border: none;
1384 | outline-offset: -1px !important;
1385 | outline: 1px solid rgb(54.006097561, 60.3597560976, 76.243902439) !important
1386 | }
1387 |
1388 | .msp-plugin .msp-slider>div:first-child {
1389 | position: absolute;
1390 | top: 0;
1391 | left: 18px;
1392 | bottom: 0;
1393 | right: 62px;
1394 | display: flex
1395 | }
1396 |
1397 | .msp-plugin .msp-slider>div:last-child {
1398 | position: absolute;
1399 | height: 32px;
1400 | line-height: 32px;
1401 | text-align: center;
1402 | right: 0;
1403 | width: 50px;
1404 | top: 0;
1405 | bottom: 0
1406 | }
1407 |
1408 | .msp-plugin .msp-slider input[type=text] {
1409 | padding-right: 6px;
1410 | padding-left: 4px;
1411 | font-size: 80%;
1412 | text-align: right
1413 | }
1414 |
1415 | .msp-plugin .msp-slider2>div:first-child {
1416 | position: absolute;
1417 | height: 32px;
1418 | line-height: 32px;
1419 | text-align: center;
1420 | left: 0;
1421 | width: 25px;
1422 | top: 0;
1423 | bottom: 0;
1424 | font-size: 80%
1425 | }
1426 |
1427 | .msp-plugin .msp-slider2>div:nth-child(2) {
1428 | position: absolute;
1429 | top: 0;
1430 | left: 35px;
1431 | bottom: 0;
1432 | right: 37px;
1433 | display: flex
1434 | }
1435 |
1436 | .msp-plugin .msp-slider2>div:last-child {
1437 | position: absolute;
1438 | height: 32px;
1439 | line-height: 32px;
1440 | text-align: center;
1441 | right: 0;
1442 | width: 25px;
1443 | top: 0;
1444 | bottom: 0;
1445 | font-size: 80%
1446 | }
1447 |
1448 | .msp-plugin .msp-slider2 input[type=text] {
1449 | padding-right: 4px;
1450 | padding-left: 4px;
1451 | font-size: 80%;
1452 | text-align: center
1453 | }
1454 |
1455 | .msp-plugin .msp-toggle-color-picker button {
1456 | border: 10px solid rgb(11.7134146341, 13.0914634146, 16.5365853659) !important;
1457 | margin: 0;
1458 | text-align: center;
1459 | padding-right: 10px;
1460 | padding-left: 10px
1461 | }
1462 |
1463 | .msp-plugin .msp-toggle-color-picker button:hover {
1464 | border-color: rgb(22.2865853659, 24.9085365854, 31.4634146341) !important;
1465 | border: none;
1466 | outline-offset: -1px !important;
1467 | outline: 1px solid rgb(54.006097561, 60.3597560976, 76.243902439) !important
1468 | }
1469 |
1470 | .msp-plugin .msp-toggle-color-picker .msp-color-picker {
1471 | position: absolute;
1472 | z-index: 100000;
1473 | background: #111318;
1474 | border-top: 1px solid #111318;
1475 | padding-bottom: 5px;
1476 | width: 100%
1477 | }
1478 |
1479 | .msp-plugin .msp-toggle-color-picker-above .msp-color-picker {
1480 | top: -85px;
1481 | height: 85px
1482 | }
1483 |
1484 | .msp-plugin .msp-toggle-color-picker-below .msp-color-picker {
1485 | top: 32px;
1486 | height: 80px
1487 | }
1488 |
1489 | .msp-plugin .msp-control-offset {
1490 | padding-left: 10px
1491 | }
1492 |
1493 | .msp-plugin .msp-accent-offset {
1494 | padding-left: 1px;
1495 | margin-left: 8px;
1496 | border-left: 2px solid rgb(214.262195122, 113.4329268293, 24.237804878)
1497 | }
1498 |
1499 | .msp-plugin .msp-control-group-wrapper {
1500 | margin-bottom: 0px;
1501 | margin-top: 1px
1502 | }
1503 |
1504 | .msp-plugin .msp-control-group-header {
1505 | background: #111318
1506 | }
1507 |
1508 | .msp-plugin .msp-control-group-header>button,
1509 | .msp-plugin .msp-control-group-header div {
1510 | padding-left: 4px;
1511 | text-align: left;
1512 | height: 24px !important;
1513 | line-height: 24px !important;
1514 | font-size: 85% !important;
1515 | background: #111318 !important;
1516 | color: hsl(216, 24.3902439024%, 68.9215686275%)
1517 | }
1518 |
1519 | .msp-plugin .msp-control-group-header .msp-icon {
1520 | height: 24px !important;
1521 | line-height: 24px !important
1522 | }
1523 |
1524 | .msp-plugin .msp-control-group-header>span {
1525 | padding-left: 5px;
1526 | line-height: 21.3333333333px;
1527 | font-size: 70%;
1528 | background: #111318;
1529 | color: hsl(216, 24.3902439024%, 68.9215686275%)
1530 | }
1531 |
1532 | .msp-plugin .msp-control-current {
1533 | background: #111318
1534 | }
1535 |
1536 | .msp-plugin .msp-control-group-footer {
1537 | background: rgb(27.5731707317, 30.8170731707, 38.9268292683);
1538 | height: 5px;
1539 | font-size: 1px;
1540 | margin-top: 1px
1541 | }
1542 |
1543 | .msp-plugin .msp-control-group-expander {
1544 | display: block;
1545 | position: absolute;
1546 | line-height: 32px;
1547 | padding: 0;
1548 | left: 0;
1549 | top: 0;
1550 | width: 120px;
1551 | text-align: left;
1552 | background: rgba(0, 0, 0, 0)
1553 | }
1554 |
1555 | .msp-plugin .msp-control-group-expander .msp-icon {
1556 | line-height: 29px;
1557 | width: 31px;
1558 | text-align: center;
1559 | font-size: 100%
1560 | }
1561 |
1562 | .msp-plugin .msp-plugin-layout_controls {
1563 | position: absolute;
1564 | left: 10px;
1565 | top: 10px
1566 | }
1567 |
1568 | .msp-plugin .msp-plugin-layout_controls>button:first-child {
1569 | margin-right: 6px
1570 | }
1571 |
1572 | .msp-plugin .msp-empty-control {
1573 | display: none
1574 | }
1575 |
1576 | .msp-plugin .msp-control .msp-btn-block,
1577 | .msp-plugin .msp-control .msp-control-row button,
1578 | .msp-plugin .msp-control-row .msp-control button {
1579 | margin-bottom: 0px;
1580 | margin-top: 0px
1581 | }
1582 |
1583 | .msp-plugin .msp-row-text {
1584 | height: 32px;
1585 | position: relative;
1586 | background: #111318;
1587 | margin-top: 1px
1588 | }
1589 |
1590 | .msp-plugin .msp-row-text>div {
1591 | line-height: 32px;
1592 | text-align: center;
1593 | color: hsl(216, 24.3902439024%, 68.9215686275%)
1594 | }
1595 |
1596 | .msp-plugin .msp-help span {
1597 | display: none
1598 | }
1599 |
1600 | .msp-plugin .msp-help:hover span {
1601 | display: inline-block;
1602 | background: linear-gradient(#111318, rgba(17, 19, 24, 0.8))
1603 | }
1604 |
1605 | .msp-plugin .msp-help-text {
1606 | position: relative;
1607 | background: #111318;
1608 | margin-top: 1px
1609 | }
1610 |
1611 | .msp-plugin .msp-help-text>div {
1612 | padding: 5px 10px;
1613 | text-align: left;
1614 | color: hsl(216, 24.3902439024%, 68.9215686275%)
1615 | }
1616 |
1617 | .msp-plugin .msp-help-text>p {
1618 | padding: 5px 10px;
1619 | text-align: left;
1620 | color: hsl(216, 24.3902439024%, 68.9215686275%)
1621 | }
1622 |
1623 | .msp-plugin .msp-help-description {
1624 | font-style: italic
1625 | }
1626 |
1627 | .msp-plugin .msp-help-legend {
1628 | padding-top: 10px
1629 | }
1630 |
1631 | .msp-plugin .msp-scale-legend>div {
1632 | width: 100%;
1633 | height: 30px
1634 | }
1635 |
1636 | .msp-plugin .msp-scale-legend>div>span {
1637 | padding: 5px;
1638 | color: #fff;
1639 | font-weight: bold;
1640 | background-color: rgba(0, 0, 0, .2)
1641 | }
1642 |
1643 | .msp-plugin .msp-table-legend>div {
1644 | margin-right: 5px;
1645 | display: inline-flex
1646 | }
1647 |
1648 | .msp-plugin .msp-table-legend>div .msp-table-legend-color {
1649 | width: 30px;
1650 | height: 20px
1651 | }
1652 |
1653 | .msp-plugin .msp-table-legend>div .msp-table-legend-text {
1654 | margin: 0 5px
1655 | }
1656 |
1657 | .msp-plugin .msp-image-preview {
1658 | position: relative;
1659 | background: #111318;
1660 | margin-top: 1px;
1661 | padding: 10px
1662 | }
1663 |
1664 | .msp-plugin .msp-image-preview canvas {
1665 | -webkit-user-select: none;
1666 | -moz-user-select: none;
1667 | -ms-user-select: none;
1668 | user-select: none
1669 | }
1670 |
1671 | .msp-plugin .msp-image-preview>span {
1672 | margin-top: 6px;
1673 | display: block;
1674 | text-align: center;
1675 | font-size: 80%;
1676 | line-height: 15px
1677 | }
1678 |
1679 | .msp-plugin .msp-copy-image-wrapper {
1680 | position: relative
1681 | }
1682 |
1683 | .msp-plugin .msp-copy-image-wrapper div {
1684 | font-weight: bold;
1685 | padding: 3px;
1686 | margin: 1px 0;
1687 | width: 100%;
1688 | background: rgb(11.7134146341, 13.0914634146, 16.5365853659);
1689 | text-align: center
1690 | }
1691 |
1692 | .msp-plugin .msp-copy-image-wrapper img {
1693 | margin-top: 1px
1694 | }
1695 |
1696 | .msp-plugin .msp-control-text-area-wrapper,
1697 | .msp-plugin .msp-text-area-wrapper {
1698 | position: relative
1699 | }
1700 |
1701 | .msp-plugin .msp-control-text-area-wrapper textarea,
1702 | .msp-plugin .msp-text-area-wrapper textarea {
1703 | border: none;
1704 | width: 100%;
1705 | height: 100%;
1706 | background: rgb(11.7134146341, 13.0914634146, 16.5365853659);
1707 | padding: 5px 10px;
1708 | resize: none;
1709 | font-size: 12px;
1710 | line-height: 16px
1711 | }
1712 |
1713 | .msp-plugin .msp-control-text-area-wrapper {
1714 | height: 64px !important
1715 | }
1716 |
1717 | .msp-plugin .msp-text-area-wrapper {
1718 | height: 96px !important
1719 | }
1720 |
1721 | .msp-plugin .msp-slider-base {
1722 | position: relative;
1723 | height: 14px;
1724 | padding: 5px 0;
1725 | width: 100%;
1726 | border-radius: 6px;
1727 | align-self: center;
1728 | box-sizing: border-box;
1729 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
1730 | }
1731 |
1732 | .msp-plugin .msp-slider-base * {
1733 | box-sizing: border-box;
1734 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
1735 | }
1736 |
1737 | .msp-plugin .msp-slider-base-rail {
1738 | position: absolute;
1739 | width: 100%;
1740 | background-color: rgb(30.7451219512, 34.362195122, 43.4048780488);
1741 | height: 4px;
1742 | border-radius: 2px
1743 | }
1744 |
1745 | .msp-plugin .msp-slider-base-track {
1746 | position: absolute;
1747 | left: 0;
1748 | height: 4px;
1749 | border-radius: 6px;
1750 | background-color: tint(#ccd4e0, 60%)
1751 | }
1752 |
1753 | .msp-plugin .msp-slider-base-handle {
1754 | position: absolute;
1755 | margin-left: -11px;
1756 | margin-top: -9px;
1757 | width: 22px;
1758 | height: 22px;
1759 | cursor: pointer;
1760 | border-radius: 50%;
1761 | background-color: #ccd4e0;
1762 | border: 4px solid rgb(30.7451219512, 34.362195122, 43.4048780488)
1763 | }
1764 |
1765 | .msp-plugin .msp-slider-base-handle:hover {
1766 | background-color: #51a2fb
1767 | }
1768 |
1769 | .msp-plugin .msp-slider-base-mark {
1770 | position: absolute;
1771 | top: 18px;
1772 | left: 0;
1773 | width: 100%;
1774 | font-size: 12px
1775 | }
1776 |
1777 | .msp-plugin .msp-slider-base-mark-text {
1778 | position: absolute;
1779 | display: inline-block;
1780 | vertical-align: middle;
1781 | text-align: center;
1782 | cursor: pointer;
1783 | color: #999
1784 | }
1785 |
1786 | .msp-plugin .msp-slider-base-mark-text-active {
1787 | color: #666
1788 | }
1789 |
1790 | .msp-plugin .msp-slider-base-step {
1791 | position: absolute;
1792 | width: 100%;
1793 | height: 4px;
1794 | background: rgba(0, 0, 0, 0)
1795 | }
1796 |
1797 | .msp-plugin .msp-slider-base-dot {
1798 | position: absolute;
1799 | bottom: -2px;
1800 | margin-left: -4px;
1801 | width: 8px;
1802 | height: 8px;
1803 | border: 2px solid #e9e9e9;
1804 | background-color: #fff;
1805 | cursor: pointer;
1806 | border-radius: 50%;
1807 | vertical-align: middle
1808 | }
1809 |
1810 | .msp-plugin .msp-slider-base-dot:first-child {
1811 | margin-left: -4px
1812 | }
1813 |
1814 | .msp-plugin .msp-slider-base-dot:last-child {
1815 | margin-left: -4px
1816 | }
1817 |
1818 | .msp-plugin .msp-slider-base-dot-active {
1819 | border-color: tint(#ccd4e0, 50%)
1820 | }
1821 |
1822 | .msp-plugin .msp-slider-base-disabled {
1823 | background: #111318;
1824 | opacity: .35
1825 | }
1826 |
1827 | .msp-plugin .msp-slider-base-disabled .msp-slider-base-handle,
1828 | .msp-plugin .msp-slider-base-disabled .msp-slider-base-dot {
1829 | cursor: not-allowed
1830 | }
1831 |
1832 | .msp-plugin .msp-slider-base-disabled .msp-slider-base-mark-text,
1833 | .msp-plugin .msp-slider-base-disabled .msp-slider-base-dot {
1834 | cursor: not-allowed !important
1835 | }
1836 |
1837 | .msp-plugin .msp-description {
1838 | padding: 10px;
1839 | font-size: 85%;
1840 | background: #111318;
1841 | text-align: center;
1842 | -webkit-user-select: none;
1843 | -moz-user-select: none;
1844 | -ms-user-select: none;
1845 | -o-user-select: none;
1846 | user-select: none;
1847 | font-weight: light;
1848 | cursor: default
1849 | }
1850 |
1851 | .msp-plugin .msp-description:not(:first-child) {
1852 | border-top: 1px solid rgb(30.7451219512, 34.362195122, 43.4048780488)
1853 | }
1854 |
1855 | .msp-plugin .msp-color-picker input {
1856 | color: #000 !important
1857 | }
1858 |
1859 | .msp-plugin .msp-no-webgl {
1860 | position: absolute;
1861 | width: 100%;
1862 | height: 100%;
1863 | left: 0;
1864 | top: 0;
1865 | display: table;
1866 | text-align: center;
1867 | background: #111318
1868 | }
1869 |
1870 | .msp-plugin .msp-no-webgl>div b {
1871 | font-size: 120%
1872 | }
1873 |
1874 | .msp-plugin .msp-no-webgl>div {
1875 | display: table-cell;
1876 | vertical-align: middle;
1877 | text-align: center;
1878 | width: 100%;
1879 | height: 100%
1880 | }
1881 |
1882 | .msp-plugin .msp-loader-msp-btn-file {
1883 | position: relative;
1884 | overflow: hidden
1885 | }
1886 |
1887 | .msp-plugin .msp-loader-msp-btn-file input[type=file] {
1888 | position: absolute;
1889 | top: 0;
1890 | right: 0;
1891 | min-width: 100%;
1892 | min-height: 100%;
1893 | font-size: 100px;
1894 | text-align: right;
1895 | filter: alpha(opacity=0);
1896 | opacity: 0;
1897 | outline: none;
1898 | background: #fff;
1899 | cursor: inherit;
1900 | display: block
1901 | }
1902 |
1903 | .msp-plugin .msp-controls-section {
1904 | margin-bottom: 10px
1905 | }
1906 |
1907 | .msp-plugin .msp-combined-color-button {
1908 | border: 4px solid rgb(11.7134146341, 13.0914634146, 16.5365853659) !important;
1909 | margin: 0;
1910 | text-align: center;
1911 | padding-right: 10px;
1912 | padding-left: 10px
1913 | }
1914 |
1915 | .msp-plugin .msp-combined-color-button:hover {
1916 | border-color: rgb(22.2865853659, 24.9085365854, 31.4634146341) !important;
1917 | border: none;
1918 | outline-offset: -1px !important;
1919 | outline: 1px solid rgb(54.006097561, 60.3597560976, 76.243902439) !important
1920 | }
1921 |
1922 | .msp-plugin .msp-combined-color-swatch {
1923 | width: 100%;
1924 | display: grid;
1925 | grid-gap: 1px;
1926 | grid-template-columns: repeat(6, auto)
1927 | }
1928 |
1929 | .msp-plugin .msp-combined-color-swatch .msp-btn:hover,
1930 | .msp-plugin .msp-combined-color-swatch .msp-control-row button:hover,
1931 | .msp-plugin .msp-control-row .msp-combined-color-swatch button:hover {
1932 | outline-offset: -1px !important;
1933 | outline: 1px solid rgb(54.006097561, 60.3597560976, 76.243902439) !important
1934 | }
1935 |
1936 | .msp-plugin .msp-action-select {
1937 | position: relative
1938 | }
1939 |
1940 | .msp-plugin .msp-action-select select {
1941 | padding-left: 42px
1942 | }
1943 |
1944 | .msp-plugin .msp-action-select option:first-child {
1945 | color: hsl(216, 24.3902439024%, 68.9215686275%)
1946 | }
1947 |
1948 | .msp-plugin .msp-action-select>.msp-icon {
1949 | display: block;
1950 | top: 0;
1951 | left: 10px;
1952 | position: absolute;
1953 | line-height: 32px
1954 | }
1955 |
1956 | .msp-plugin .msp-simple-help-section {
1957 | height: 28px;
1958 | line-height: 28px;
1959 | margin-top: 5px;
1960 | margin-bottom: 5px;
1961 | padding: 0 10px;
1962 | font-weight: 500;
1963 | background: #111318;
1964 | color: #ccd4e0
1965 | }
1966 |
1967 | .msp-plugin .msp-left-panel-controls-buttons {
1968 | position: absolute;
1969 | width: 32px;
1970 | top: 0;
1971 | bottom: 0;
1972 | padding-top: 10px;
1973 | background: #111318
1974 | }
1975 |
1976 | .msp-plugin .msp-left-panel-controls-buttons-bottom {
1977 | position: absolute;
1978 | bottom: 0
1979 | }
1980 |
1981 | .msp-plugin .msp-left-panel-controls-button-data-dirty {
1982 | position: absolute;
1983 | width: 6px;
1984 | height: 6px;
1985 | background: rgb(214.262195122, 113.4329268293, 24.237804878);
1986 | border-radius: 3px;
1987 | right: 6px;
1988 | bottom: 6px
1989 | }
1990 |
1991 | .msp-plugin .msp-left-panel-controls .msp-scrollable-container {
1992 | left: 33px
1993 | }
1994 |
1995 | .msp-plugin .msp-mapped-parameter-group {
1996 | position: relative
1997 | }
1998 |
1999 | .msp-plugin .msp-mapped-parameter-group>.msp-control-row:first-child>div:nth-child(2) {
2000 | right: 33px
2001 | }
2002 |
2003 | .msp-plugin .msp-mapped-parameter-group>button:first-child {
2004 | right: 33px
2005 | }
2006 |
2007 | .msp-plugin .msp-mapped-parameter-group>.msp-btn-icon {
2008 | position: absolute;
2009 | right: 0;
2010 | width: 32px;
2011 | top: 0;
2012 | padding: 0
2013 | }
2014 |
2015 | .msp-plugin .msp-shape-filled {
2016 | fill: #ccd4e0;
2017 | stroke: #ccd4e0
2018 | }
2019 |
2020 | .msp-plugin .msp-shape-empty {
2021 | fill: none;
2022 | stroke: #ccd4e0
2023 | }
2024 |
2025 | .msp-plugin .msp-no-overflow {
2026 | overflow: hidden;
2027 | text-overflow: ellipsis;
2028 | white-space: nowrap
2029 | }
2030 |
2031 | .msp-plugin .msp-25-lower-contrast-text {
2032 | color: hsl(216, 24.3902439024%, 58.9215686275%)
2033 | }
2034 |
2035 | .msp-plugin .msp-expandable-group-color-stripe {
2036 | position: absolute;
2037 | left: 0;
2038 | top: 30px;
2039 | width: 120px;
2040 | height: 2px
2041 | }
2042 |
2043 | .msp-plugin .msp-section-header {
2044 | height: 32px;
2045 | line-height: 32px;
2046 | margin-top: 10px;
2047 | margin-bottom: 10px;
2048 | text-align: right;
2049 | padding: 0 10px;
2050 | font-weight: bold;
2051 | background: #111318;
2052 | overflow: hidden;
2053 | cursor: default
2054 | }
2055 |
2056 | .msp-plugin .msp-section-header>.msp-icon {
2057 | display: block;
2058 | float: left
2059 | }
2060 |
2061 | .msp-plugin .msp-section-header>small {
2062 | font-weight: normal
2063 | }
2064 |
2065 | .msp-plugin .msp-current-header {
2066 | height: 32px;
2067 | line-height: 32px;
2068 | margin-bottom: 10px;
2069 | text-align: center;
2070 | font-weight: bold;
2071 | background: #111318
2072 | }
2073 |
2074 | .msp-plugin .msp-flex-row,
2075 | .msp-plugin .msp-state-image-row {
2076 | margin-top: 1px;
2077 | background: #111318;
2078 | display: flex;
2079 | flex-direction: row;
2080 | width: inherit;
2081 | height: 32px
2082 | }
2083 |
2084 | .msp-plugin .msp-flex-row>.msp-flex-item,
2085 | .msp-plugin .msp-state-image-row>.msp-flex-item {
2086 | margin: 0;
2087 | flex: 1 1 auto;
2088 | margin-right: 1px;
2089 | overflow: hidden
2090 | }
2091 |
2092 | .msp-plugin .msp-flex-row>.msp-flex-item:last-child,
2093 | .msp-plugin .msp-state-image-row>.msp-flex-item:last-child {
2094 | margin-right: 0
2095 | }
2096 |
2097 | .msp-plugin .msp-flex-row>select,
2098 | .msp-plugin .msp-state-image-row>select,
2099 | .msp-plugin .msp-flex-row>button,
2100 | .msp-plugin .msp-state-image-row>button {
2101 | margin: 0;
2102 | flex: 1 1 auto;
2103 | margin-right: 1px;
2104 | height: 32px;
2105 | overflow: hidden
2106 | }
2107 |
2108 | .msp-plugin .msp-flex-row .msp-btn-icon,
2109 | .msp-plugin .msp-state-image-row .msp-btn-icon,
2110 | .msp-plugin .msp-flex-row .msp-btn-icon-small,
2111 | .msp-plugin .msp-state-image-row .msp-btn-icon-small {
2112 | flex: 0 0 32px;
2113 | max-width: 32px
2114 | }
2115 |
2116 | .msp-plugin .msp-flex-row>select,
2117 | .msp-plugin .msp-state-image-row>select {
2118 | background: none
2119 | }
2120 |
2121 | .msp-plugin .msp-flex-row>select>option[value=_],
2122 | .msp-plugin .msp-state-image-row>select>option[value=_] {
2123 | display: none
2124 | }
2125 |
2126 | .msp-plugin .msp-flex-row>select:last-child,
2127 | .msp-plugin .msp-state-image-row>select:last-child,
2128 | .msp-plugin .msp-flex-row>button:last-child,
2129 | .msp-plugin .msp-state-image-row>button:last-child {
2130 | margin-right: 0
2131 | }
2132 |
2133 | .msp-plugin .msp-flex-row>button.msp-control-button-label,
2134 | .msp-plugin .msp-state-image-row>button.msp-control-button-label {
2135 | background: #111318
2136 | }
2137 |
2138 | .msp-plugin .msp-state-list {
2139 | list-style: none
2140 | }
2141 |
2142 | .msp-plugin .msp-state-list>li {
2143 | position: relative;
2144 | overflow: hidden
2145 | }
2146 |
2147 | .msp-plugin .msp-state-list>li>button:first-child {
2148 | text-align: left;
2149 | border-left: 10px solid rgb(42.3756097561, 47.3609756098, 59.8243902439) !important
2150 | }
2151 |
2152 | .msp-plugin .msp-state-list>li>div {
2153 | position: absolute;
2154 | right: 0;
2155 | top: 0
2156 | }
2157 |
2158 | .msp-plugin .msp-state-image-row {
2159 | height: 96px;
2160 | margin-top: 0px
2161 | }
2162 |
2163 | .msp-plugin .msp-state-image-row>button {
2164 | height: 96px;
2165 | padding: 0px
2166 | }
2167 |
2168 | .msp-plugin .msp-state-image-row>button>img {
2169 | min-height: 96px;
2170 | width: inherit;
2171 | transform: translateY(-50%);
2172 | top: 50%;
2173 | position: relative
2174 | }
2175 |
2176 | .msp-plugin .msp-tree-row {
2177 | position: relative;
2178 | margin-top: 0;
2179 | margin-bottom: 1px;
2180 | background: rgba(0, 0, 0, 0)
2181 | }
2182 |
2183 | .msp-plugin .msp-tree-row-current .msp-btn-tree-label>span {
2184 | font-weight: bold
2185 | }
2186 |
2187 | .msp-plugin .msp-tree-row-current .msp-btn-tree-label {
2188 | border-radius: 0 !important
2189 | }
2190 |
2191 | .msp-plugin .msp-tree-row .msp-btn-tree-label {
2192 | text-align: left;
2193 | border-radius: 0 0 0 8px;
2194 | border-left-width: 4px;
2195 | border-left-style: solid
2196 | }
2197 |
2198 | .msp-plugin .msp-tree-row .msp-btn-tree-label>small {
2199 | color: hsl(216, 24.3902439024%, 63.9215686275%)
2200 | }
2201 |
2202 | .msp-plugin .msp-tree-updates-wrapper .msp-control-group-header:last-child {
2203 | margin-bottom: 1px
2204 | }
2205 |
2206 | .msp-plugin .msp-viewport-top-left-controls {
2207 | position: absolute;
2208 | left: 10px;
2209 | top: 10px
2210 | }
2211 |
2212 | .msp-plugin .msp-viewport-top-left-controls .msp-traj-controls {
2213 | line-height: 32px;
2214 | float: left;
2215 | margin-right: 10px;
2216 | background-color: rgb(11.7134146341, 13.0914634146, 16.5365853659)
2217 | }
2218 |
2219 | .msp-plugin .msp-viewport-top-left-controls .msp-traj-controls>span {
2220 | color: #ccd4e0;
2221 | margin-left: 10px;
2222 | margin-right: 10px;
2223 | font-size: 85%;
2224 | display: inline-block
2225 | }
2226 |
2227 | .msp-plugin .msp-viewport-top-left-controls .msp-state-snapshot-viewport-controls {
2228 | line-height: 32px;
2229 | float: left;
2230 | margin-right: 10px
2231 | }
2232 |
2233 | .msp-plugin .msp-viewport-top-left-controls .msp-state-snapshot-viewport-controls>button {
2234 | background-color: rgb(11.7134146341, 13.0914634146, 16.5365853659)
2235 | }
2236 |
2237 | .msp-plugin .msp-viewport-top-left-controls .msp-state-snapshot-viewport-controls>select {
2238 | display: inline-block;
2239 | width: 200px;
2240 | margin-right: 10px
2241 | }
2242 |
2243 | .msp-plugin .msp-viewport-top-left-controls .msp-animation-viewport-controls {
2244 | line-height: 32px;
2245 | float: left;
2246 | margin-right: 10px;
2247 | position: relative
2248 | }
2249 |
2250 | .msp-plugin .msp-viewport-top-left-controls .msp-animation-viewport-controls>div:first-child {
2251 | position: relative;
2252 | display: inline-block
2253 | }
2254 |
2255 | .msp-plugin .msp-viewport-top-left-controls .msp-animation-viewport-controls>div:first-child>button {
2256 | position: relative
2257 | }
2258 |
2259 | .msp-plugin .msp-viewport-top-left-controls .msp-animation-viewport-controls .msp-animation-viewport-controls-select {
2260 | width: 290px;
2261 | position: absolute;
2262 | left: 0;
2263 | margin-top: 10px;
2264 | background: rgb(30.7451219512, 34.362195122, 43.4048780488)
2265 | }
2266 |
2267 | .msp-plugin .msp-viewport-top-left-controls .msp-animation-viewport-controls .msp-animation-viewport-controls-select .msp-control-row:first-child {
2268 | margin-top: 0
2269 | }
2270 |
2271 | .msp-plugin .msp-selection-viewport-controls {
2272 | position: relative;
2273 | margin: 10px auto 0 auto;
2274 | width: 430px
2275 | }
2276 |
2277 | .msp-plugin .msp-selection-viewport-controls-actions {
2278 | position: absolute;
2279 | width: 100%;
2280 | top: 32px;
2281 | background: rgb(30.7451219512, 34.362195122, 43.4048780488)
2282 | }
2283 |
2284 | .msp-plugin .msp-selection-viewport-controls>.msp-flex-row .msp-btn,
2285 | .msp-plugin .msp-selection-viewport-controls>.msp-state-image-row .msp-btn,
2286 | .msp-plugin .msp-selection-viewport-controls>.msp-flex-row .msp-control-row button,
2287 | .msp-plugin .msp-control-row .msp-selection-viewport-controls>.msp-flex-row button,
2288 | .msp-plugin .msp-selection-viewport-controls>.msp-state-image-row .msp-control-row button,
2289 | .msp-plugin .msp-control-row .msp-selection-viewport-controls>.msp-state-image-row button {
2290 | padding: 0 5px
2291 | }
2292 |
2293 | .msp-plugin .msp-selection-viewport-controls select.msp-form-control,
2294 | .msp-plugin .msp-selection-viewport-controls select.msp-btn,
2295 | .msp-plugin .msp-selection-viewport-controls .msp-control-row select,
2296 | .msp-plugin .msp-control-row .msp-selection-viewport-controls select {
2297 | padding: 0 5px;
2298 | text-align: center;
2299 | background: rgb(11.7134146341, 13.0914634146, 16.5365853659);
2300 | flex: 0 0 80px;
2301 | text-overflow: ellipsis
2302 | }
2303 |
2304 | .msp-plugin .msp-param-object-list-item {
2305 | margin-top: 1px;
2306 | position: relative
2307 | }
2308 |
2309 | .msp-plugin .msp-param-object-list-item>button {
2310 | text-align: left
2311 | }
2312 |
2313 | .msp-plugin .msp-param-object-list-item>button>span {
2314 | font-weight: bold
2315 | }
2316 |
2317 | .msp-plugin .msp-param-object-list-item>div {
2318 | position: absolute;
2319 | right: 0;
2320 | top: 0
2321 | }
2322 |
2323 | .msp-plugin .msp-state-actions .msp-transform-wrapper:last-child {
2324 | margin-bottom: 10px
2325 | }
2326 |
2327 | .msp-plugin .msp-button-row {
2328 | display: flex;
2329 | flex-direction: row;
2330 | height: 32px;
2331 | width: inherit
2332 | }
2333 |
2334 | .msp-plugin .msp-button-row>button {
2335 | margin: 0;
2336 | flex: 1 1 auto;
2337 | margin-right: 1px;
2338 | height: 32px;
2339 | text-align-last: center;
2340 | background: none;
2341 | padding: 0 10px;
2342 | overflow: hidden
2343 | }
2344 |
2345 | .msp-plugin .msp-action-menu-options-no-header,
2346 | .msp-plugin .msp-action-menu-options .msp-control-group-children {
2347 | max-height: 300px;
2348 | overflow: hidden;
2349 | overflow-y: auto
2350 | }
2351 |
2352 | .msp-plugin .msp-action-menu-options .msp-control-row,
2353 | .msp-plugin .msp-action-menu-options button,
2354 | .msp-plugin .msp-action-menu-options .msp-icon,
2355 | .msp-plugin .msp-action-menu-options .msp-flex-row,
2356 | .msp-plugin .msp-action-menu-options .msp-state-image-row {
2357 | height: 24px;
2358 | line-height: 24px
2359 | }
2360 |
2361 | .msp-plugin .msp-action-menu-options button {
2362 | text-align: left
2363 | }
2364 |
2365 | .msp-plugin .msp-action-menu-options .msp-action-menu-button {
2366 | margin-top: 1px;
2367 | display: flex
2368 | }
2369 |
2370 | .msp-plugin .msp-action-menu-options .msp-action-menu-button .msp-icon {
2371 | margin-right: 6px
2372 | }
2373 |
2374 | .msp-plugin .msp-representation-entry {
2375 | position: relative
2376 | }
2377 |
2378 | .msp-plugin .msp-representation-entry>.msp-control-group-header>.msp-btn,
2379 | .msp-plugin .msp-control-row .msp-representation-entry>.msp-control-group-header>button {
2380 | font-weight: bold
2381 | }
2382 |
2383 | .msp-plugin .msp-representation-entry>.msp-control-group-header>.msp-icon,
2384 | .msp-plugin .msp-representation-entry>.msp-control-group-header>.msp-btn-link {
2385 | line-height: 24px;
2386 | height: 24px
2387 | }
2388 |
2389 | .msp-plugin .msp-control-group-presets-wrapper {
2390 | position: absolute;
2391 | right: 0;
2392 | top: 0
2393 | }
2394 |
2395 | .msp-plugin .msp-control-group-presets-wrapper .msp-control-group-header {
2396 | background: rgba(0, 0, 0, 0)
2397 | }
2398 |
2399 | .msp-plugin .msp-control-group-presets-wrapper button {
2400 | background: rgba(0, 0, 0, 0) !important
2401 | }
2402 |
2403 | .msp-plugin .msp-parameter-matrix input {
2404 | flex: 1 1 auto;
2405 | min-width: 0
2406 | }
2407 |
2408 | .msp-plugin .msp-btn-apply-simple {
2409 | text-align: left
2410 | }
2411 |
2412 | .msp-plugin .msp-btn-apply-simple .msp-icon {
2413 | margin-right: 10px
2414 | }
2415 |
2416 | .msp-plugin .msp-type-class-Root {
2417 | border-left-color: #111318
2418 | }
2419 |
2420 | .msp-plugin .msp-type-class-Group {
2421 | border-left-color: rgb(214.262195122, 113.4329268293, 24.237804878)
2422 | }
2423 |
2424 | .msp-plugin .msp-type-class-Data {
2425 | border-left-color: hsl(183.5294117647, 8.7179487179%, 46.7647058824%)
2426 | }
2427 |
2428 | .msp-plugin .msp-type-class-Object {
2429 | border-left-color: rgb(36.616, 162.384, 89.948)
2430 | }
2431 |
2432 | .msp-plugin .msp-type-class-Representation3D {
2433 | border-left-color: rgb(36.9790794979, 139.6987447699, 208.5209205021)
2434 | }
2435 |
2436 | .msp-plugin .msp-type-class-Behavior {
2437 | border-left-color: rgb(127.949790795, 67.1966527197, 152.8033472803)
2438 | }
2439 |
2440 | .msp-plugin .msp-accent-color-cyan {
2441 | color: hsl(183.5294117647, 8.7179487179%, 46.7647058824%)
2442 | }
2443 |
2444 | .msp-plugin .msp-accent-bg-cyan {
2445 | background: hsl(183.5294117647, 8.7179487179%, 46.7647058824%)
2446 | }
2447 |
2448 | .msp-plugin .msp-transform-header-brand-cyan {
2449 | border-bottom: 1px solid hsl(183.5294117647, 8.7179487179%, 46.7647058824%)
2450 | }
2451 |
2452 | .msp-plugin .msp-transform-header-brand-cyan:active,
2453 | .msp-plugin .msp-transform-header-brand-cyan:focus {
2454 | border-bottom: 1px solid hsl(183.5294117647, 8.7179487179%, 46.7647058824%)
2455 | }
2456 |
2457 | .msp-plugin .msp-accent-color-red {
2458 | color: rgb(190.9931506849, 39.1780821918, 23.5068493151)
2459 | }
2460 |
2461 | .msp-plugin .msp-accent-bg-red {
2462 | background: rgb(190.9931506849, 39.1780821918, 23.5068493151)
2463 | }
2464 |
2465 | .msp-plugin .msp-transform-header-brand-red {
2466 | border-bottom: 1px solid rgb(190.9931506849, 39.1780821918, 23.5068493151)
2467 | }
2468 |
2469 | .msp-plugin .msp-transform-header-brand-red:active,
2470 | .msp-plugin .msp-transform-header-brand-red:focus {
2471 | border-bottom: 1px solid rgb(190.9931506849, 39.1780821918, 23.5068493151)
2472 | }
2473 |
2474 | .msp-plugin .msp-accent-color-gray {
2475 | color: rgb(33.8356164384, 47.5, 61.1643835616)
2476 | }
2477 |
2478 | .msp-plugin .msp-accent-bg-gray {
2479 | background: rgb(33.8356164384, 47.5, 61.1643835616)
2480 | }
2481 |
2482 | .msp-plugin .msp-transform-header-brand-gray {
2483 | border-bottom: 1px solid rgb(33.8356164384, 47.5, 61.1643835616)
2484 | }
2485 |
2486 | .msp-plugin .msp-transform-header-brand-gray:active,
2487 | .msp-plugin .msp-transform-header-brand-gray:focus {
2488 | border-bottom: 1px solid rgb(33.8356164384, 47.5, 61.1643835616)
2489 | }
2490 |
2491 | .msp-plugin .msp-accent-color-green {
2492 | color: rgb(36.616, 162.384, 89.948)
2493 | }
2494 |
2495 | .msp-plugin .msp-accent-bg-green {
2496 | background: rgb(36.616, 162.384, 89.948)
2497 | }
2498 |
2499 | .msp-plugin .msp-transform-header-brand-green {
2500 | border-bottom: 1px solid rgb(36.616, 162.384, 89.948)
2501 | }
2502 |
2503 | .msp-plugin .msp-transform-header-brand-green:active,
2504 | .msp-plugin .msp-transform-header-brand-green:focus {
2505 | border-bottom: 1px solid rgb(36.616, 162.384, 89.948)
2506 | }
2507 |
2508 | .msp-plugin .msp-accent-color-purple {
2509 | color: rgb(127.949790795, 67.1966527197, 152.8033472803)
2510 | }
2511 |
2512 | .msp-plugin .msp-accent-bg-purple {
2513 | background: rgb(127.949790795, 67.1966527197, 152.8033472803)
2514 | }
2515 |
2516 | .msp-plugin .msp-transform-header-brand-purple {
2517 | border-bottom: 1px solid rgb(127.949790795, 67.1966527197, 152.8033472803)
2518 | }
2519 |
2520 | .msp-plugin .msp-transform-header-brand-purple:active,
2521 | .msp-plugin .msp-transform-header-brand-purple:focus {
2522 | border-bottom: 1px solid rgb(127.949790795, 67.1966527197, 152.8033472803)
2523 | }
2524 |
2525 | .msp-plugin .msp-accent-color-blue {
2526 | color: rgb(36.9790794979, 139.6987447699, 208.5209205021)
2527 | }
2528 |
2529 | .msp-plugin .msp-accent-bg-blue {
2530 | background: rgb(36.9790794979, 139.6987447699, 208.5209205021)
2531 | }
2532 |
2533 | .msp-plugin .msp-transform-header-brand-blue {
2534 | border-bottom: 1px solid rgb(36.9790794979, 139.6987447699, 208.5209205021)
2535 | }
2536 |
2537 | .msp-plugin .msp-transform-header-brand-blue:active,
2538 | .msp-plugin .msp-transform-header-brand-blue:focus {
2539 | border-bottom: 1px solid rgb(36.9790794979, 139.6987447699, 208.5209205021)
2540 | }
2541 |
2542 | .msp-plugin .msp-accent-color-orange {
2543 | color: rgb(214.262195122, 113.4329268293, 24.237804878)
2544 | }
2545 |
2546 | .msp-plugin .msp-accent-bg-orange {
2547 | background: rgb(214.262195122, 113.4329268293, 24.237804878)
2548 | }
2549 |
2550 | .msp-plugin .msp-transform-header-brand-orange {
2551 | border-bottom: 1px solid rgb(214.262195122, 113.4329268293, 24.237804878)
2552 | }
2553 |
2554 | .msp-plugin .msp-transform-header-brand-orange:active,
2555 | .msp-plugin .msp-transform-header-brand-orange:focus {
2556 | border-bottom: 1px solid rgb(214.262195122, 113.4329268293, 24.237804878)
2557 | }
2558 |
2559 | .msp-plugin .msp-volume-channel-inline-controls>:first-child {
2560 | position: absolute;
2561 | left: 0;
2562 | top: 0;
2563 | height: 32px;
2564 | right: 32px
2565 | }
2566 |
2567 | .msp-plugin .msp-volume-channel-inline-controls .msp-slider>div:first-child {
2568 | right: 42px
2569 | }
2570 |
2571 | .msp-plugin .msp-volume-channel-inline-controls .msp-slider>div:last-child {
2572 | width: 30px
2573 | }
2574 |
2575 | .msp-plugin .msp-volume-channel-inline-controls>button {
2576 | position: absolute;
2577 | right: 0;
2578 | width: 32px;
2579 | top: 0;
2580 | padding: 0
2581 | }
2582 |
2583 | .msp-plugin .msp-volume-channel-inline-controls>button .msp-material-icon {
2584 | margin-right: 0
2585 | }
2586 |
2587 | .msp-plugin .msp-list-unstyled {
2588 | padding-left: 0;
2589 | list-style: none
2590 | }
2591 |
2592 | .msp-plugin .msp-drag-drop-overlay {
2593 | border: 12px dashed #ccd4e0;
2594 | background: rgba(0, 0, 0, .36);
2595 | display: flex;
2596 | align-items: center;
2597 | justify-content: center;
2598 | position: absolute;
2599 | left: 0;
2600 | right: 0;
2601 | top: 0;
2602 | bottom: 0;
2603 | font-size: 48px;
2604 | font-weight: bold
2605 | }
2606 |
2607 | .msp-plugin .msp-task-state {
2608 | line-height: 32px
2609 | }
2610 |
2611 | .msp-plugin .msp-task-state>span {
2612 | -webkit-user-select: none;
2613 | -moz-user-select: none;
2614 | -ms-user-select: none;
2615 | -o-user-select: none;
2616 | user-select: none;
2617 | cursor: default
2618 | }
2619 |
2620 | .msp-plugin .msp-overlay-tasks {
2621 | position: absolute;
2622 | display: flex;
2623 | top: 0;
2624 | left: 0;
2625 | bottom: 0;
2626 | right: 0;
2627 | height: 100%;
2628 | width: 100%;
2629 | z-index: 1000;
2630 | justify-content: center;
2631 | align-items: center;
2632 | background: rgba(0, 0, 0, .25)
2633 | }
2634 |
2635 | .msp-plugin .msp-overlay-tasks .msp-task-state>div {
2636 | height: 32px;
2637 | margin-top: 1px;
2638 | position: relative;
2639 | width: 100%;
2640 | background: #111318
2641 | }
2642 |
2643 | .msp-plugin .msp-overlay-tasks .msp-task-state>div>div {
2644 | height: 32px;
2645 | line-height: 32px;
2646 | display: inline-block;
2647 | padding: 0 10px;
2648 | -webkit-user-select: none;
2649 | -moz-user-select: none;
2650 | -ms-user-select: none;
2651 | -o-user-select: none;
2652 | user-select: none;
2653 | cursor: default;
2654 | white-space: nowrap;
2655 | background: #111318;
2656 | position: absolute
2657 | }
2658 |
2659 | .msp-plugin .msp-overlay-tasks .msp-task-state>div>button {
2660 | display: inline-block;
2661 | margin-top: -3px
2662 | }
2663 |
2664 | .msp-plugin .msp-background-tasks {
2665 | position: absolute;
2666 | left: 0;
2667 | bottom: 0;
2668 | z-index: 1000
2669 | }
2670 |
2671 | .msp-plugin .msp-background-tasks .msp-task-state>div {
2672 | height: 32px;
2673 | margin-top: 1px;
2674 | position: relative;
2675 | width: 100%;
2676 | background: #111318
2677 | }
2678 |
2679 | .msp-plugin .msp-background-tasks .msp-task-state>div>div {
2680 | height: 32px;
2681 | line-height: 32px;
2682 | display: inline-block;
2683 | padding: 0 10px;
2684 | -webkit-user-select: none;
2685 | -moz-user-select: none;
2686 | -ms-user-select: none;
2687 | -o-user-select: none;
2688 | user-select: none;
2689 | cursor: default;
2690 | white-space: nowrap;
2691 | background: #111318;
2692 | position: absolute
2693 | }
2694 |
2695 | .msp-plugin .msp-background-tasks .msp-task-state>div>button {
2696 | display: inline-block;
2697 | margin-top: -3px
2698 | }
2699 |
2700 | .msp-plugin .msp-viewport {
2701 | position: absolute;
2702 | left: 0;
2703 | top: 0;
2704 | right: 0;
2705 | bottom: 0;
2706 | background: #111318
2707 | }
2708 |
2709 | .msp-plugin .msp-viewport .msp-btn-link {
2710 | background: rgba(0, 0, 0, .2)
2711 | }
2712 |
2713 | .msp-plugin .msp-viewport-expanded {
2714 | position: fixed;
2715 | z-index: 1000
2716 | }
2717 |
2718 | .msp-plugin .msp-viewport-host3d {
2719 | position: absolute;
2720 | left: 0;
2721 | top: 0;
2722 | right: 0;
2723 | bottom: 0;
2724 | -webkit-user-select: none;
2725 | user-select: none;
2726 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
2727 | -webkit-touch-callout: none;
2728 | touch-action: manipulation
2729 | }
2730 |
2731 | .msp-plugin .msp-viewport-host3d>canvas {
2732 | background-color: #111318;
2733 | background-image: linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%, lightgrey), linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%, lightgrey);
2734 | background-size: 60px 60px;
2735 | background-position: 0 0, 30px 30px
2736 | }
2737 |
2738 | .msp-plugin .msp-viewport-controls {
2739 | position: absolute;
2740 | right: 10px;
2741 | top: 10px;
2742 | width: 32px
2743 | }
2744 |
2745 | .msp-plugin .msp-viewport-controls-buttons {
2746 | text-align: right;
2747 | position: relative
2748 | }
2749 |
2750 | .msp-plugin .msp-viewport-controls-buttons>div {
2751 | position: relative;
2752 | margin-bottom: 4px
2753 | }
2754 |
2755 | .msp-plugin .msp-viewport-controls-buttons button {
2756 | padding: 0;
2757 | text-align: center;
2758 | width: 32px;
2759 | position: relative
2760 | }
2761 |
2762 | .msp-plugin .msp-viewport-controls-buttons .msp-btn-link-toggle-off {
2763 | color: hsl(216, 24.3902439024%, 50.9215686275%)
2764 | }
2765 |
2766 | .msp-plugin .msp-viewport-controls-buttons .msp-btn-link:hover {
2767 | color: #51a2fb
2768 | }
2769 |
2770 | .msp-plugin .msp-semi-transparent-background {
2771 | background: #111318;
2772 | opacity: .5;
2773 | position: absolute;
2774 | top: 0;
2775 | left: 0;
2776 | width: 100%;
2777 | height: 100%
2778 | }
2779 |
2780 | .msp-plugin .msp-hover-box-wrapper {
2781 | position: relative
2782 | }
2783 |
2784 | .msp-plugin .msp-hover-box-wrapper .msp-hover-box-body {
2785 | visibility: hidden;
2786 | position: absolute;
2787 | right: 36px;
2788 | top: 0;
2789 | width: 100px;
2790 | background-color: #111318
2791 | }
2792 |
2793 | .msp-plugin .msp-hover-box-wrapper .msp-hover-box-spacer {
2794 | visibility: hidden;
2795 | position: absolute;
2796 | right: 32px;
2797 | top: 0;
2798 | width: 4px;
2799 | height: 32px
2800 | }
2801 |
2802 | .msp-plugin .msp-hover-box-wrapper:hover .msp-hover-box-body,
2803 | .msp-plugin .msp-hover-box-wrapper:hover .msp-hover-box-spacer {
2804 | visibility: visible
2805 | }
2806 |
2807 | .msp-plugin .msp-viewport-controls-panel {
2808 | width: 290px;
2809 | top: 0;
2810 | right: 36px;
2811 | position: absolute;
2812 | background: rgb(30.7451219512, 34.362195122, 43.4048780488)
2813 | }
2814 |
2815 | .msp-plugin .msp-viewport-controls-panel .msp-control-group-wrapper:first-child {
2816 | padding-top: 0
2817 | }
2818 |
2819 | .msp-plugin .msp-viewport-controls-panel .msp-viewport-controls-panel-controls {
2820 | overflow-y: auto;
2821 | max-height: 400px
2822 | }
2823 |
2824 | .msp-plugin .msp-highlight-toast-wrapper {
2825 | position: absolute;
2826 | right: 10px;
2827 | bottom: 10px;
2828 | max-width: 95%;
2829 | z-index: 10000
2830 | }
2831 |
2832 | .msp-plugin .msp-highlight-info {
2833 | color: #51a2fb;
2834 | padding: 3px 10px;
2835 | background: #111318;
2836 | opacity: 90%;
2837 | max-width: 400px;
2838 | -webkit-user-select: none;
2839 | -moz-user-select: none;
2840 | -ms-user-select: none;
2841 | -o-user-select: none;
2842 | user-select: none;
2843 | cursor: default
2844 | }
2845 |
2846 | .msp-plugin .msp-highlight-markdown-row {
2847 | padding-left: 10px
2848 | }
2849 |
2850 | .msp-plugin .msp-highlight-simple-row {
2851 | text-align: right
2852 | }
2853 |
2854 | .msp-plugin .msp-highlight-info-hr {
2855 | margin-inline: 0px;
2856 | margin-block: 3px;
2857 | border: none;
2858 | height: 1px;
2859 | background-color: #51a2fb
2860 | }
2861 |
2862 | .msp-plugin .msp-highlight-info-additional {
2863 | font-size: 85%;
2864 | display: inline-block;
2865 | color: rgb(5.1685393258, 109.8314606742, 224.8314606742)
2866 | }
2867 |
2868 | .msp-plugin .msp-snapshot-description-wrapper {
2869 | background: rgba(17, 19, 24, .5);
2870 | position: absolute;
2871 | left: 0;
2872 | top: 42px;
2873 | padding: 6.6px 10px;
2874 | max-height: 224px;
2875 | overflow: hidden;
2876 | overflow-y: auto;
2877 | max-width: 400px
2878 | }
2879 |
2880 | .msp-plugin .msp-snapshot-description-wrapper a {
2881 | text-decoration: underline;
2882 | cursor: pointer;
2883 | color: #ccd4e0
2884 | }
2885 |
2886 | .msp-plugin .msp-snapshot-description-wrapper ul,
2887 | .msp-plugin .msp-snapshot-description-wrapper ol {
2888 | padding-left: 14px
2889 | }
2890 |
2891 | .msp-plugin .msp-log-wrap {
2892 | position: absolute;
2893 | right: 0;
2894 | top: 0;
2895 | left: 0;
2896 | bottom: 0;
2897 | overflow: hidden
2898 | }
2899 |
2900 | .msp-plugin .msp-log {
2901 | position: absolute;
2902 | right: -20px;
2903 | top: 0;
2904 | left: 0;
2905 | bottom: 0;
2906 | overflow-y: scroll;
2907 | overflow-x: hidden;
2908 | font-size: 90%;
2909 | background: rgb(30.7451219512, 34.362195122, 43.4048780488)
2910 | }
2911 |
2912 | .msp-plugin .msp-log {
2913 | font-size: 90%
2914 | }
2915 |
2916 | .msp-plugin .msp-log ul {
2917 | padding: 0;
2918 | margin: 0
2919 | }
2920 |
2921 | .msp-plugin .msp-log {
2922 | color: hsl(216, 24.3902439024%, 78.9215686275%)
2923 | }
2924 |
2925 | .msp-plugin .msp-log li {
2926 | clear: both;
2927 | margin: 0;
2928 | background: #111318;
2929 | position: relative
2930 | }
2931 |
2932 | .msp-plugin .msp-log li:not(:last-child) {
2933 | border-bottom: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049)
2934 | }
2935 |
2936 | .msp-plugin .msp-log .msp-log-entry {
2937 | margin-left: 110px;
2938 | background: rgb(20.1719512195, 22.5451219512, 28.4780487805);
2939 | padding: 3px 25px 3px 10px
2940 | }
2941 |
2942 | .msp-plugin .msp-log .msp-log-timestamp {
2943 | padding: 3px 10px 3px 10px;
2944 | float: left;
2945 | text-align: right;
2946 | width: 110px;
2947 | color: hsl(216, 24.3902439024%, 63.9215686275%);
2948 | font-size: 100%
2949 | }
2950 |
2951 | .msp-plugin .msp-log .msp-log-timestamp small {
2952 | font-size: 100%
2953 | }
2954 |
2955 | .msp-plugin .msp-log .label {
2956 | margin-top: -3px;
2957 | font-size: 7pt
2958 | }
2959 |
2960 | .msp-plugin .msp-log-entry-badge {
2961 | position: absolute;
2962 | left: 0;
2963 | top: 0;
2964 | bottom: 0;
2965 | width: 6px
2966 | }
2967 |
2968 | .msp-plugin .msp-log-entry-message {
2969 | background: #0cca5d
2970 | }
2971 |
2972 | .msp-plugin .msp-log-entry-info {
2973 | background: #5e3673
2974 | }
2975 |
2976 | .msp-plugin .msp-log-entry-error {
2977 | background: #fd354b
2978 | }
2979 |
2980 | .msp-plugin .msp-log-entry-warning {
2981 | background: #fcc937
2982 | }
2983 |
2984 | .msp-plugin .msp-sequence {
2985 | position: absolute;
2986 | right: 0;
2987 | top: 0;
2988 | left: 0;
2989 | bottom: 0;
2990 | background: #111318
2991 | }
2992 |
2993 | .msp-plugin .msp-sequence-select {
2994 | position: relative;
2995 | height: 24px;
2996 | width: 100%;
2997 | margin-bottom: 1px;
2998 | background: rgb(30.7451219512, 34.362195122, 43.4048780488);
2999 | text-align: left
3000 | }
3001 |
3002 | .msp-plugin .msp-sequence-select>span {
3003 | display: inline-block;
3004 | line-height: 24px;
3005 | padding: 0 10px;
3006 | font-size: 85%;
3007 | font-weight: bold;
3008 | cursor: default
3009 | }
3010 |
3011 | .msp-plugin .msp-sequence-select>select {
3012 | display: inline-block;
3013 | max-width: 120px;
3014 | width: auto;
3015 | text-overflow: ellipsis;
3016 | font-size: 85%;
3017 | height: 24px;
3018 | line-height: 24px;
3019 | background-size: 6px 8px;
3020 | background-color: rgb(30.7451219512, 34.362195122, 43.4048780488)
3021 | }
3022 |
3023 | .msp-plugin .msp-sequence-wrapper {
3024 | word-break: break-word;
3025 | padding: 10px 10px 3px 10px;
3026 | user-select: none
3027 | }
3028 |
3029 | .msp-plugin .msp-sequence-wrapper-non-empty {
3030 | font-size: 85%;
3031 | line-height: 180%;
3032 | font-family: "Courier New", monospace;
3033 | background: rgb(11.7134146341, 13.0914634146, 16.5365853659);
3034 | width: 100%;
3035 | overflow-y: auto;
3036 | overflow-x: hidden;
3037 | position: absolute;
3038 | top: 25px;
3039 | left: 0;
3040 | bottom: 0;
3041 | right: 0
3042 | }
3043 |
3044 | .msp-plugin .msp-sequence-chain-label {
3045 | margin-left: 10px;
3046 | margin-top: 10px;
3047 | user-select: none;
3048 | color: #51a2fb;
3049 | font-size: 90%;
3050 | line-height: 90%;
3051 | padding-left: .2em
3052 | }
3053 |
3054 | .msp-plugin .msp-sequence-wrapper span {
3055 | cursor: pointer
3056 | }
3057 |
3058 | .msp-plugin .msp-sequence-wrapper .msp-sequence-residue-long {
3059 | margin: 0em .2em 0em .2em
3060 | }
3061 |
3062 | .msp-plugin .msp-sequence-wrapper .msp-sequence-residue-long-begin {
3063 | margin: 0em .2em 0em 0em
3064 | }
3065 |
3066 | .msp-plugin .msp-sequence-wrapper .msp-sequence-label {
3067 | color: #51a2fb;
3068 | font-size: 90%;
3069 | line-height: 90%;
3070 | padding-bottom: 1em;
3071 | padding-left: .2em
3072 | }
3073 |
3074 | .msp-plugin .msp-sequence-wrapper .msp-sequence-number {
3075 | color: #51a2fb;
3076 | word-break: keep-all;
3077 | cursor: default;
3078 | position: relative;
3079 | top: -1.1em;
3080 | left: 3.1em;
3081 | padding: 0px;
3082 | margin-left: -3em;
3083 | font-size: 80%;
3084 | pointer-events: none
3085 | }
3086 |
3087 | .msp-plugin .msp-sequence-wrapper .msp-sequence-number-long {
3088 | left: 3.3em
3089 | }
3090 |
3091 | .msp-plugin .msp-sequence-wrapper .msp-sequence-number-long-negative {
3092 | left: 2.7em
3093 | }
3094 |
3095 | .msp-plugin .msp-sequence-wrapper .msp-sequence-number-negative {
3096 | left: 2.5em
3097 | }
3098 |
3099 | .msp-plugin .msp-sequence-wrapper .msp-sequence-present {
3100 | color: #ccd4e0
3101 | }
3102 |
3103 | .msp-plugin .msp-sequence-wrapper .msp-sequence-missing {
3104 | color: hsl(216, 24.3902439024%, 38.9215686275%);
3105 | cursor: default
3106 | }
3107 |
3108 | .msp-plugin .msp-transformer .msp-entity-badge {
3109 | position: absolute;
3110 | top: 0;
3111 | right: 0;
3112 | height: 32px;
3113 | line-height: 32px;
3114 | width: 32px
3115 | }
3116 |
3117 | .msp-plugin .msp-layout-right,
3118 | .msp-plugin .msp-layout-left {
3119 | background: rgb(30.7451219512, 34.362195122, 43.4048780488)
3120 | }
3121 |
3122 | .msp-plugin .msp-transformer-wrapper {
3123 | position: relative
3124 | }
3125 |
3126 | .msp-plugin .msp-transformer-wrapper .msp-entity-badge {
3127 | left: 0;
3128 | top: 0
3129 | }
3130 |
3131 | .msp-plugin .msp-transformer-wrapper:first-child .msp-panel-description-content {
3132 | top: 33px
3133 | }
3134 |
3135 | .msp-plugin .msp-transformer-wrapper:not(:first-child) .msp-panel-description-content {
3136 | bottom: 33px
3137 | }
3138 |
3139 | .msp-plugin .msp-transform-wrapper {
3140 | margin-bottom: 10px
3141 | }
3142 |
3143 | .msp-plugin .msp-transform-wrapper-collapsed {
3144 | margin-bottom: 1px
3145 | }
3146 |
3147 | .msp-plugin .msp-transform-update-wrapper {
3148 | margin-bottom: 1px
3149 | }
3150 |
3151 | .msp-plugin .msp-transform-update-wrapper-collapsed {
3152 | margin-bottom: 1px
3153 | }
3154 |
3155 | .msp-plugin .msp-transform-update-wrapper>.msp-transform-header>button,
3156 | .msp-plugin .msp-transform-update-wrapper-collapsed>.msp-transform-header>button {
3157 | text-align: left;
3158 | padding-left: 32px;
3159 | line-height: 24px;
3160 | background: rgb(22.2865853659, 24.9085365854, 31.4634146341)
3161 | }
3162 |
3163 | .msp-plugin .msp-transform-wrapper>.msp-transform-header>button {
3164 | text-align: left;
3165 | background: #111318;
3166 | font-weight: bold;
3167 | padding-right: 5px
3168 | }
3169 |
3170 | .msp-plugin .msp-transform-header {
3171 | position: relative
3172 | }
3173 |
3174 | .msp-plugin .msp-transform-header>button>small {
3175 | font-weight: normal;
3176 | float: right
3177 | }
3178 |
3179 | .msp-plugin .msp-transform-header>button>span:first-child {
3180 | margin-right: 10px
3181 | }
3182 |
3183 | .msp-plugin .msp-transform-header>button:hover {
3184 | color: hsl(216, 24.3902439024%, 68.9215686275%)
3185 | }
3186 |
3187 | .msp-plugin .msp-transform-header-brand {
3188 | margin-bottom: -1px
3189 | }
3190 |
3191 | .msp-plugin .msp-transform-header-brand svg {
3192 | fill: #ccd4e0;
3193 | stroke: #ccd4e0
3194 | }
3195 |
3196 | .msp-plugin .msp-transform-default-params {
3197 | background: #111318;
3198 | position: absolute;
3199 | left: 0;
3200 | top: 0;
3201 | width: 32px;
3202 | padding: 0
3203 | }
3204 |
3205 | .msp-plugin .msp-transform-default-params:hover {
3206 | background: hsl(222.8571428571, 17.0731707317%, -11.9607843137%)
3207 | }
3208 |
3209 | .msp-plugin .msp-transform-apply-wrap {
3210 | position: relative;
3211 | margin-top: 1px;
3212 | width: 100%;
3213 | height: 32px
3214 | }
3215 |
3216 | .msp-plugin .msp-transform-refresh {
3217 | width: 87px;
3218 | margin-left: 33px;
3219 | background: #111318;
3220 | text-align: right
3221 | }
3222 |
3223 | .msp-plugin .msp-transform-apply {
3224 | display: block;
3225 | position: absolute;
3226 | left: 120px;
3227 | right: 0;
3228 | top: 0
3229 | }
3230 |
3231 | .msp-plugin .msp-transform-apply-wider {
3232 | margin-left: 33px
3233 | }
3234 |
3235 | .msp-plugin .msp-data-beh {
3236 | margin: 10px 0 !important
3237 | }
3238 |
3239 | .msp-plugin .msp-toast-container {
3240 | position: relative;
3241 | z-index: 1001
3242 | }
3243 |
3244 | .msp-plugin .msp-toast-container .msp-toast-entry {
3245 | color: #ccd4e0;
3246 | background: rgb(30.7451219512, 34.362195122, 43.4048780488);
3247 | position: relative;
3248 | float: right;
3249 | min-height: 32px;
3250 | margin-top: 10px;
3251 | border: 1px solid rgb(48.7195121951, 54.4512195122, 68.7804878049);
3252 | display: table
3253 | }
3254 |
3255 | .msp-plugin .msp-toast-container .msp-toast-entry .msp-toast-title {
3256 | height: 100%;
3257 | line-height: 32px;
3258 | padding: 0 10px;
3259 | background: #111318;
3260 | font-weight: bold;
3261 | display: table-cell;
3262 | -webkit-user-select: none;
3263 | -moz-user-select: none;
3264 | -ms-user-select: none;
3265 | -o-user-select: none;
3266 | user-select: none;
3267 | font-weight: light;
3268 | cursor: pointer
3269 | }
3270 |
3271 | .msp-plugin .msp-toast-container .msp-toast-entry .msp-toast-message {
3272 | padding: 3px 42px 3px 10px;
3273 | display: table-cell
3274 | }
3275 |
3276 | .msp-plugin .msp-toast-container .msp-toast-entry .msp-toast-message a {
3277 | text-decoration: none;
3278 | color: #68befd;
3279 | font-weight: bold
3280 | }
3281 |
3282 | .msp-plugin .msp-toast-container .msp-toast-entry .msp-toast-message a:hover {
3283 | text-decoration: underline;
3284 | color: hsl(205.3691275168, 97.385620915%, 50%)
3285 | }
3286 |
3287 | .msp-plugin .msp-toast-container .msp-toast-entry .msp-toast-message a:active,
3288 | .msp-plugin .msp-toast-container .msp-toast-entry .msp-toast-message a:focus {
3289 | color: #68befd;
3290 | outline-offset: 0;
3291 | outline: none
3292 | }
3293 |
3294 | .msp-plugin .msp-toast-container .msp-toast-entry .msp-toast-hide {
3295 | position: absolute;
3296 | width: 42px;
3297 | right: 0;
3298 | top: 0;
3299 | bottom: 0
3300 | }
3301 |
3302 | .msp-plugin .msp-toast-container .msp-toast-entry .msp-toast-hide .msp-btn-icon {
3303 | background: rgba(0, 0, 0, 0);
3304 | position: absolute;
3305 | top: 1px;
3306 | right: 0;
3307 | left: 0;
3308 | bottom: 0;
3309 | width: 100%;
3310 | text-align: right;
3311 | padding-right: 5px
3312 | }
3313 |
3314 | .msp-plugin .msp-help-row {
3315 | position: relative;
3316 | height: 32px;
3317 | background: #111318;
3318 | margin-top: 1px;
3319 | display: table;
3320 | width: 100%
3321 | }
3322 |
3323 | .msp-plugin .msp-help-row>span {
3324 | width: 120px;
3325 | text-align: right;
3326 | padding: 3px 10px;
3327 | color: hsl(216, 24.3902439024%, 68.9215686275%);
3328 | display: table-cell;
3329 | font-weight: bold;
3330 | -webkit-user-select: none;
3331 | -moz-user-select: none;
3332 | -ms-user-select: none;
3333 | -o-user-select: none;
3334 | user-select: none;
3335 | cursor: default
3336 | }
3337 |
3338 | .msp-plugin .msp-help-row>div {
3339 | background: rgb(11.7134146341, 13.0914634146, 16.5365853659);
3340 | position: relative;
3341 | padding: 3px 10px;
3342 | display: table-cell
3343 | }
3344 |
3345 | .msp-plugin .msp-canvas {
3346 | width: 100%;
3347 | height: 100%;
3348 | background-color: #f3f2ee
3349 | }
3350 |
3351 | .msp-plugin .msp-canvas text {
3352 | -webkit-touch-callout: none;
3353 | -webkit-user-select: none;
3354 | -khtml-user-select: none;
3355 | -moz-user-select: none;
3356 | -ms-user-select: none;
3357 | user-select: none
3358 | }
3359 |
3360 | .msp-plugin .msp-canvas circle {
3361 | stroke: #000;
3362 | stroke-width: 10;
3363 | stroke-opacity: .3
3364 | }
3365 |
3366 | .msp-plugin .msp-canvas circle:hover {
3367 | fill: #ae5d04;
3368 | stroke: #000;
3369 | stroke-width: 10px
3370 | }
3371 |
3372 | .msp-plugin .msp-canvas .info {
3373 | fill: #fff;
3374 | stroke: #000;
3375 | stroke-width: 3
3376 | }
3377 |
3378 | .msp-plugin .msp-canvas .show {
3379 | visibility: visible
3380 | }
3381 |
3382 | .msp-plugin .msp-canvas .hide {
3383 | visibility: hidden
3384 | }
3385 |
3386 | .msp-plugin .msp-canvas .delete-button rect {
3387 | fill: #ed4337;
3388 | stroke: #000
3389 | }
3390 |
3391 | .msp-plugin .msp-canvas .delete-button text {
3392 | stroke: #fff;
3393 | fill: #fff
3394 | }
3395 |
3396 | .msp-plugin .msp-canvas .delete-button:hover {
3397 | stroke: #000;
3398 | stroke-width: 3;
3399 | fill: #ff6961
3400 | }
3401 |
3402 | .msp-plugin .msp-canvas .infoCircle:hover {
3403 | fill: #4c66b2
3404 | }
3405 |
3406 | .msp-plugin .msp-canvas:focus {
3407 | outline: none
3408 | }
3409 |
3410 | .msp-plugin .msp-logo {
3411 | display: block;
3412 | position: absolute;
3413 | bottom: 10px;
3414 | right: 10px;
3415 | height: 32px;
3416 | width: 100px;
3417 | background-repeat: no-repeat;
3418 | background-position: bottom right;
3419 | background-size: auto;
3420 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFcAAAAgCAYAAABn7+QVAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACHDwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKL2lDQ1BJQ0MgUHJvZmlsZQAASMedlndUVNcWh8+9d3qhzTACUobeu8AA0nuTXkVhmBlgKAMOMzSxIaICEUVEmiJIUMSA0VAkVkSxEBRUsAckCCgxGEVULG9G1ouurLz38vL746xv7bP3ufvsvc9aFwCSpy+XlwZLAZDKE/CDPJzpEZFRdOwAgAEeYIApAExWRrpfsHsIEMnLzYWeIXICXwQB8HpYvAJw09AzgE4H/5+kWel8geiYABGbszkZLBEXiDglS5Auts+KmBqXLGYYJWa+KEERy4k5YZENPvsssqOY2ak8tojFOaezU9li7hXxtkwhR8SIr4gLM7mcLBHfErFGijCVK+I34thUDjMDABRJbBdwWIkiNhExiR8S5CLi5QDgSAlfcdxXLOBkC8SXcklLz+FzExIFdB2WLt3U2ppB9+RkpXAEAsMAJiuZyWfTXdJS05m8HAAW7/xZMuLa0kVFtjS1trQ0NDMy/apQ/3Xzb0rc20V6Gfi5ZxCt/4vtr/zSGgBgzIlqs/OLLa4KgM4tAMjd+2LTOACApKhvHde/ug9NPC+JAkG6jbFxVlaWEZfDMhIX9A/9T4e/oa++ZyQ+7o/y0F058UxhioAurhsrLSVNyKdnpDNZHLrhn4f4Hwf+dR4GQZx4Dp/DE0WEiaaMy0sQtZvH5gq4aTw6l/efmvgPw/6kxbkWidL4EVBjjIDUdSpAfu0HKAoRINH7xV3/o2+++DAgfnnhKpOLc//vN/1nwaXiJYOb8DnOJSiEzhLyMxf3xM8SoAEBSAIqkAfKQB3oAENgBqyALXAEbsAb+IMQEAlWAxZIBKmAD7JAHtgECkEx2An2gGpQBxpBM2gFx0EnOAXOg0vgGrgBboP7YBRMgGdgFrwGCxAEYSEyRIHkIRVIE9KHzCAGZA+5Qb5QEBQJxUIJEA8SQnnQZqgYKoOqoXqoGfoeOgmdh65Ag9BdaAyahn6H3sEITIKpsBKsBRvDDNgJ9oFD4FVwArwGzoUL4B1wJdwAH4U74PPwNfg2PAo/g+cQgBARGqKKGCIMxAXxR6KQeISPrEeKkAqkAWlFupE+5CYyiswgb1EYFAVFRxmibFGeqFAUC7UGtR5VgqpGHUZ1oHpRN1FjqFnURzQZrYjWR9ugvdAR6AR0FroQXYFuQrejL6JvoyfQrzEYDA2jjbHCeGIiMUmYtZgSzD5MG+YcZhAzjpnDYrHyWH2sHdYfy8QKsIXYKuxR7FnsEHYC+wZHxKngzHDuuCgcD5ePq8AdwZ3BDeEmcQt4Kbwm3gbvj2fjc/Cl+EZ8N/46fgK/QJAmaBPsCCGEJMImQiWhlXCR8IDwkkgkqhGtiYFELnEjsZJ4jHiZOEZ8S5Ih6ZFcSNEkIWkH6RDpHOku6SWZTNYiO5KjyALyDnIz+QL5EfmNBEXCSMJLgi2xQaJGokNiSOK5JF5SU9JJcrVkrmSF5AnJ65IzUngpLSkXKabUeqkaqZNSI1Jz0hRpU2l/6VTpEukj0lekp2SwMloybjJsmQKZgzIXZMYpCEWd4kJhUTZTGikXKRNUDFWb6kVNohZTv6MOUGdlZWSXyYbJZsvWyJ6WHaUhNC2aFy2FVko7ThumvVuitMRpCWfJ9iWtS4aWzMstlXOU48gVybXJ3ZZ7J0+Xd5NPlt8l3yn/UAGloKcQqJClsF/hosLMUupS26WspUVLjy+9pwgr6ikGKa5VPKjYrzinpKzkoZSuVKV0QWlGmabsqJykXK58RnlahaJir8JVKVc5q/KULkt3oqfQK+m99FlVRVVPVaFqveqA6oKatlqoWr5am9pDdYI6Qz1evVy9R31WQ0XDTyNPo0XjniZek6GZqLlXs09zXktbK1xrq1an1pS2nLaXdq52i/YDHbKOg84anQadW7oYXYZusu4+3Rt6sJ6FXqJejd51fVjfUp+rv09/0ABtYG3AM2gwGDEkGToZZhq2GI4Z0Yx8jfKNOo2eG2sYRxnvMu4z/mhiYZJi0mhy31TG1Ns037Tb9HczPTOWWY3ZLXOyubv5BvMu8xfL9Jdxlu1fdseCYuFnsdWix+KDpZUl37LVctpKwyrWqtZqhEFlBDBKGJet0dbO1husT1m/tbG0Edgct/nN1tA22faI7dRy7eWc5Y3Lx+3U7Jh29Xaj9nT7WPsD9qMOqg5MhwaHx47qjmzHJsdJJ12nJKejTs+dTZz5zu3O8y42Lutczrkirh6uRa4DbjJuoW7Vbo/c1dwT3FvcZz0sPNZ6nPNEe/p47vIc8VLyYnk1e816W3mv8+71IfkE+1T7PPbV8+X7dvvBft5+u/0erNBcwVvR6Q/8vfx3+z8M0A5YE/BjICYwILAm8EmQaVBeUF8wJTgm+Ejw6xDnkNKQ+6E6ocLQnjDJsOiw5rD5cNfwsvDRCOOIdRHXIhUiuZFdUdiosKimqLmVbiv3rJyItogujB5epb0qe9WV1QqrU1afjpGMYcaciEXHhsceiX3P9Gc2MOfivOJq42ZZLqy9rGdsR3Y5e5pjxynjTMbbxZfFTyXYJexOmE50SKxInOG6cKu5L5I8k+qS5pP9kw8lf0oJT2lLxaXGpp7kyfCSeb1pymnZaYPp+umF6aNrbNbsWTPL9+E3ZUAZqzK6BFTRz1S/UEe4RTiWaZ9Zk/kmKyzrRLZ0Ni+7P0cvZ3vOZK577rdrUWtZa3vyVPM25Y2tc1pXvx5aH7e+Z4P6hoINExs9Nh7eRNiUvOmnfJP8svxXm8M3dxcoFWwsGN/isaWlUKKQXziy1XZr3TbUNu62ge3m26u2fyxiF10tNimuKH5fwiq5+o3pN5XffNoRv2Og1LJ0/07MTt7O4V0Ouw6XSZfllo3v9tvdUU4vLyp/tSdmz5WKZRV1ewl7hXtHK30ru6o0qnZWva9OrL5d41zTVqtYu712fh9739B+x/2tdUp1xXXvDnAP3Kn3qO9o0GqoOIg5mHnwSWNYY9+3jG+bmxSaips+HOIdGj0cdLi32aq5+YjikdIWuEXYMn00+uiN71y/62o1bK1vo7UVHwPHhMeefh/7/fBxn+M9JxgnWn/Q/KG2ndJe1AF15HTMdiZ2jnZFdg2e9D7Z023b3f6j0Y+HTqmeqjkte7r0DOFMwZlPZ3PPzp1LPzdzPuH8eE9Mz/0LERdu9Qb2Dlz0uXj5kvulC31OfWcv210+dcXmysmrjKud1yyvdfRb9Lf/ZPFT+4DlQMd1q+tdN6xvdA8uHzwz5DB0/qbrzUu3vG5du73i9uBw6PCdkeiR0TvsO1N3U+6+uJd5b+H+xgfoB0UPpR5WPFJ81PCz7s9to5ajp8dcx/ofBz++P84af/ZLxi/vJwqekJ9UTKpMNk+ZTZ2adp++8XTl04ln6c8WZgp/lf619rnO8x9+c/ytfzZiduIF/8Wn30teyr889GrZq565gLlHr1NfL8wXvZF/c/gt423fu/B3kwtZ77HvKz/ofuj+6PPxwafUT5/+BQOY8/xvJtwPAAAACXBIWXMAAC4iAAAuIgGq4t2SAAANMElEQVRoQ92aB1xURx7H/69sY5eOFBELCipESsSC0RCMJRZMrICHGiMmGjWaqDk7YEsuGok5TS6xi56KGtsFG6jBiAYLKhqVc8GGBZG+fd97N+/twNJWFksS7/v5DG/nN/OG/fze/838Z4CA/wMCE9d9W8oQ3mUMBSojBTqWAuBQAweHIC56lanXHw8xJixM6qhQNcX1KuQykluyKzMPVxvF5XUh3hIpgFSiQz8AJBItSKU6sCsX55P9byLxxRKwYl3W5O6dg5o62IMRmcpyBBz87wNYcyH3R4iL+gh3+8MhHaTqYJKUKO2dPYTigIqza1MlLZLnzh3arQ/uZzVn14YOIGRyJWXrqgR5U6VI1kRJS92VBEEry+wrAnC3F04XL3cY4OMF7/p6weC2zSDQzQG3/IlM7dspdPmU0VxtLqYf5haM6HYOBYLVUwcXByQy92JxXioexUzFhT5cySn3TrjrC4WP3EsPHuPfZGJVZg4HCdt/wF0aT8LWUHT/jTpl4fZU3KNBSHytQ0D33uDR0qfjoqg3hmOpQU65d4u2cW4X6NCyJ1ZeIeKSFRC3p1q4kzYdmzr6Zk98p6rsj+rhi0KoFe5gIm53M/ypDhbNJQgC3kbTFUGSi+LiwmgsWyQ5zk9McESCZ8gEVHvF1kneWJI5CJT2SHWDbUQ0vNbEvqr4OClwCyZ+RzSQ+psomqOwUgOL5vL4BIdCi/aBvtJb3AdYsoirs0usnWfH1vbNOmPlFWHmWlve2DFB3t0nhvh0qm2wRRZuG+ksFyUlDe4qcbYRJ0H8v6NxSxVPNZcnPPJDIAlY8PWnXWVYqsPhZb3lDAfzW3T50xbmZ+MfyFhbRcr7yNj1EZ1gdb+O8DFvMKk7it4+ywYjY11k0s1po8KpmA4tITUmnHaWS5HBKJKr0aC5zXw6QJvgNzyhXDIZS3UgCN3UJq3fdLd188PKs3H8+Bjpvn2x/jv2TwnbsOezt3/YPavTss3TXXHzi4U3Vic/+H5gq+7rkLEkmgb5yWwVb3CnNiFAcD+aOtaGaMobmzrqLaoyIwlC11RkNB/JvPGCiGjQXJ43h8QCSRGzEqeG1Xmah77u48QCPdM7NBYrjSPveJg069i7H2UcjUpndWSZrZ3bFRfHlic8nL1TnezcM2Vyh0dLtsbnzdu8JHHW5qVt8G3Pj9qOT4RYluOE/UYllQZPCvFxMik1cbGRSKsbWwlKUPhxhDGxZJ25Ls28oX2X3k60HmZiqQqDTj+rqX8fB7lTC6xYT2569zA9Jb5m7xz8r3aB03uE9fpOFP7WYujZ/TPo22MSDOs1FT4ePBfG9ZvQsod/12kUJf190prli4YnJ6Mt2HOSMKICGLL/5su3Tn6wPxMYZE4lvMH/RAZP6NjaJGBsJSJIi3mrTg6d9bAYem05YSxS6WJgQdR2LFtnLk9oxFigRaKpq2aEuWMJDizu6UlQosltuo3FivU8zgyOkEhkRzz941u2CogDxyYhgMzDrWb4rMXN0Q36vN4TZr43XuTt0WyeoiR/MwqV509JqgzOSx+77zcw8nGM4UMx2r+5qYJpqpByHVztcc3E+QdFXJWx8dE78MgCDaZYldi5eIB/jwj577/+NB9VJ/GajmHj2nYZKpPZNW5aVJ9v2ULDwlaXdsvFYlvzpo1l9PD4yXUoKStAY3MgFjuAexNvcFA4C+32NgqY3HcofHFg18ioH1adRSHyjdBgCQJaQ/y2SFyzAIMKuSkp+1YAepIOGwZ1Bgo9UGu4gCK2z9ZfoEit3yMI1X8XxZwh+B2al2/7jOnfbsKqGaNeB7RYgmsAmvJi2LHkbwaC0baXyElKKpVe7f/JVlpsY4978Abp0PxsvqcSVVZfMGoud3Z44+HZ8vOeG2m3GWOkntNwK8CTgky4eiWJK9fqflUZJRe0jFirZmgvDSPu29or2PmdzhEgpkVC3/ziIpiRvL1ETUua74+NLed3aEnRg4IC3F2Edp6DNx/AmqxcXLMeFK0w3M8L1yxToTfCtCNZUKTRY8VMZv4TyC/VxFiM3OM7N0BudiaMW/g9VgBkto7QIWyYKDstaSEYGdo3dEQNY/n5/EbKJHBq2QPcOozBWk24K00UGgM3QuI2GisA5cVXIOdyYqHeKBo0cEDSaSwLLNu8TJ5968o6LQORI3oMETRPRycI9GrhkHH7Di/UjQpEvzYeQnlZKMQ0rB1Y/25+xO4M2Fl61/KcazTo4W5ONuRcOIUVEx3CI0Fqax8lljsO9w2tuTMuyksHVcHvwKHX2xIcU9aFsgmQEbR5MX50aztQYJzWu19NY3lmjp6pekIrxmbfvv6woLQQqwCBzZujn0SYqfbX5KkLGprVL51IXgMcW5VdgFgqh4DwkaR/WAxBi837Co5j4Hbmj3wucglL9cJy4ENKzRkVf5+q9Bqnpol9WKpDYuR0DfoKabcL8rGCotfBEQ0GLy41ewk81VyWIfYV3lNmXj2NNizVaNvtPfBBc2B1Hl07BKqi2xkkyf0HSxYg0D7eFn9G5rJ69EAYfXj4zgos1QtaYoq16G2qRCYWA0dw5oFqcb9cAyfvPG50ufq4FI/wdPg5t777+VKoNh1ZPzVbIAiWIwl69qm9G9Lad+kJFF5QKFosXCthjXrI/W0jsCw5G62+Tz0D5p8mU3sxrp7FWwClZKYcHWMawvKqvuf6PZh86HwBusW6VY0g/FzlEru0mHAsPB05mnN3X7sHKzNz+K91Df2o+VQIorDBVGz2lpPHvhobdvRy+v7ewT2HYrUmdy/tBU3po5Ren55MP7e+a6MP2F8aHLHXqr9ExO8Y46oQr08bFS6cflkD/1gT+wYLH1aeydGCSD8Q5ox5Ymo1YdUmgqTI2ZkpWziDToMVM0adCpRntrAERc/B0qvFImSsrWAsWdvYx/j1rkRtYNBGo+bbk9gnGKZ19Q0GgzgVlm4yJeQYq8ydsfb4eW158a6LaTuxYkaZuQN0mrLtb39y/KkL2V+Shdved7URrz9Wj7Fn7xfBuAOZuGbiTqkKRu09Y8HgtkFg5A3+qcpgq8zloUT0vItpyUZthXlq0amKQfnbTgNw5AIsvTos3o2SYGL10vAA0r8eY/mdV4nWgBUz26/eqWMwz7JeQeDrbIcM1idgyXpzp6xOyzHoVBuyUrdiBeD6ySQw6DVr+n9+XImlBmE5ggHOiGs8wleg0G7e8urEQwBNEuavywjpYY2BGse8oQ9QHjgM7bK0/ApfiWDslhOGEq1+NZZqwnH526/cOVbdYP7K13OelKcBY/O5ICKsNpeHFJMJ1zL2aVQlBaAqfgDKswdUKIFYhJutAqVqDznDI1xDdbRVFkkc6YzDQ9piqX448HNSmE+jitVq/mkU4OqzERd9sEJnGNJ/W7pgcGalsTp9FDLRdF5QGwJ0wNpEoAhOi0GGao0M8Fe+DkzpIEgYpMY9G2fuxMRj+axBvyrryEbITtsIjNGwcuDnvzzEzVahJ+gsVnURfTK/Vg6uYUDSNH8gVG/0Ltqy6E2FVNajjYf5WFNZ8AhQcvb88zxvsIEZzBvcV4hYYyQsiP4Jt9YPbyAycgcytM2qn4G/moz9qMpYnkaZK0CIv8y9cKQk72JqkYqAZVi1GmlAxXVGX3DdWHYGKwDurSLBxrb1yLRDo/ftTxkflpQyxW5lyhTJ97vm+azYNneWiCJ+HtxtICnCeTZ/wH0m9yaQHHNAEJ6X+ZGHeINLtLpIiIusP2JrwxspJyLyyzVL+WttY3kabe74xCNFBMd+xXDcl2MTfinBcqPggP5Kfe+bqimTomTwWkg8tPaNjLC3bX5CxtKljjqxViGzyfFrFfTFB/3GK3w9zTvd49eyobCsNGPvlCl1ziKeGWQwxI2sYWx2QamwsFWWcQfO4hbM9EgNLIiaK1zrofGRy8PQ34o1mmf+Hyz5/nub9Kprh4qVS4WzBR6SFEOLVv3hze7zYOiAFTDqveUQ03829O0yDJrYm8+Lr9+/AztOn1SxHPNy/xoqklxEi9qAo7kPq0rGvcIBaOIah3s0yDOZO/rro6rIxDP1Pi1rIBKABb3tiIqCw0fzL38GmvKbuMUyOoMODmf9Ct8d3l3CsfpByR9Pu4KbXg5zhjxBUZlSp8yPPoF7NIhwWG5jb5/h16kbltBrShLw+K4SCvOVCYt2no7HslWg7e9iW5fWcxVNvIGmGVMRGYEoO4zmykLhsBx3heTk4VSgW+lENSObQ8n9POSOHUEi90L97dHOlQKtXg9FFSVwu+A+XLmbx5Tp2F1qhvr7d7Ezb+MhBPjD8tdbNA+SSGSgYwmUGpFwo7AczuYX/an/iEdM6B3qKqbZAbguIKJQEZEosYSLi3efzsKyVZxd3/V1Cc0FisQMGsMAUqkBXfXoqgXChjlgF/LAfCiLOXfuQ5G2tDRcY5CGaRhxO41R4qJlRJSaEZVrjOLbapY6Z9BASkJswn18Sw2CVqx/t5ghncoZElQsBTqm8u+X3A0UaRm48gcD8D/XZskfp8IFSwAAAABJRU5ErkJggg==)
3421 | }
3422 |
3423 | .msp-plugin .msp-plugin-content {
3424 | color: #ccd4e0
3425 | }
3426 |
3427 | .msp-plugin .msp-plugin-init-error {
3428 | white-space: pre;
3429 | margin: 10px
3430 | }
3431 |
3432 | .msp-plugin .msp-svg-text {
3433 | fill: #ccd4e0
3434 | }
3435 |
3436 | .msp-plugin {
3437 | background: #111318
3438 | }
3439 |
3440 | .msp-plugin ::-webkit-scrollbar-thumb {
3441 | background-color: rgba(128, 128, 128, .5019607843);
3442 | border-radius: 10px;
3443 | border: solid 1px rgba(0, 0, 0, 0);
3444 | background-clip: content-box
3445 | }
3446 |
3447 | .msp-plugin .msp-plugin-init-error {
3448 | color: gray
3449 | }
3450 |
3451 | .msp-plugin .msp-layout-static {
3452 | container-type: size
3453 | }
3454 |
3455 | .msp-plugin .msp-viewport-controls-panel .msp-viewport-controls-panel-controls {
3456 | max-height: calc(100cqh - 41px - 24px - 10px)
3457 | }
3458 |
3459 | .msp-plugin .msp-simple-help-section {
3460 | overflow: hidden;
3461 | text-wrap: nowrap;
3462 | text-overflow: ellipsis
3463 | }
3464 |
3465 | .msp-plugin .msp-control-group-header button {
3466 | overflow: hidden;
3467 | text-wrap: nowrap;
3468 | text-overflow: ellipsis
3469 | }
3470 |
3471 | .msp-plugin .pdbemolstar-custom-control-viewport-top-left {
3472 | float: left
3473 | }
3474 |
3475 | .msp-plugin .pdbemolstar-custom-control-viewport-top-left:not(:empty) {
3476 | margin-right: 10px
3477 | }
3478 |
3479 | .msp-plugin .pdbemolstar-viewport-top-center-controls {
3480 | width: 100%;
3481 | display: flex;
3482 | flex-direction: column;
3483 | align-items: center;
3484 | padding-inline: 74px;
3485 | pointer-events: none
3486 | }
3487 |
3488 | .msp-plugin .pdbemolstar-viewport-top-center-controls>* {
3489 | margin-top: 10px;
3490 | pointer-events: auto;
3491 | max-width: 100%
3492 | }
3493 |
3494 | .msp-plugin .pdbemolstar-overlay {
3495 | z-index: 1000;
3496 | position: absolute;
3497 | inset: 0px;
3498 | display: flex;
3499 | justify-content: center;
3500 | align-items: center;
3501 | pointer-events: none
3502 | }
3503 |
3504 | .msp-plugin .pdbemolstar-overlay .pdbemolstar-overlay-box {
3505 | width: 25%;
3506 | height: 25%
3507 | }
3508 |
3509 | .msp-plugin .pdbemolstar-overlay svg.pdbe-animated-logo {
3510 | background-color: rgba(0, 0, 0, 0);
3511 | width: 100%;
3512 | height: 100%;
3513 | opacity: 80%
3514 | }
3515 |
3516 | .msp-plugin .pdbemolstar-overlay svg.pdbe-animated-logo .path-fg {
3517 | stroke-dasharray: 1812 250;
3518 | stroke-dashoffset: -250;
3519 | animation: dash linear normal infinite;
3520 | animation-duration: 5s;
3521 | animation-delay: 1s
3522 | }
3523 |
3524 | @keyframes dash {
3525 | 0% {
3526 | stroke-dashoffset: 1812
3527 | }
3528 |
3529 | 80% {
3530 | stroke-dashoffset: -250
3531 | }
3532 |
3533 | 100% {
3534 | stroke-dashoffset: -250
3535 | }
3536 | }
3537 |
3538 | .msp-plugin .pdbemolstar-state-gallery-controls {
3539 | overflow-x: hidden;
3540 | word-wrap: break-word
3541 | }
3542 |
3543 | .msp-plugin .pdbemolstar-state-gallery-controls:focus-visible {
3544 | outline: none
3545 | }
3546 |
3547 | .msp-plugin .pdbemolstar-state-gallery-controls .pdbemolstar-state-gallery-state-button {
3548 | height: 24px;
3549 | line-height: 24px;
3550 | padding-inline: 5px;
3551 | text-align: left;
3552 | overflow: hidden;
3553 | text-overflow: ellipsis
3554 | }
3555 |
3556 | .msp-plugin .pdbemolstar-state-gallery-controls .pdbemolstar-state-gallery-state-button .msp-icon {
3557 | margin-right: 3px
3558 | }
3559 |
3560 | .msp-plugin .pdbemolstar-state-gallery-controls .pdbemolstar-state-gallery-legend {
3561 | margin-block: 8px
3562 | }
3563 |
3564 | .msp-plugin .pdbemolstar-state-gallery-controls .pdbemolstar-state-gallery-legend .image_legend_li {
3565 | margin-left: 16px
3566 | }
3567 |
3568 | .msp-plugin .pdbemolstar-state-gallery-controls .pdbemolstar-state-gallery-legend .highlight {
3569 | font-weight: bold
3570 | }
3571 |
3572 | .msp-plugin .pdbemolstar-state-gallery-title-box {
3573 | width: 500px;
3574 | max-width: 100%;
3575 | background-color: rgb(11.7134146341, 13.0914634146, 16.5365853659);
3576 | display: flex;
3577 | flex-direction: row;
3578 | justify-content: space-between;
3579 | align-items: stretch
3580 | }
3581 |
3582 | .msp-plugin .pdbemolstar-state-gallery-title-box .msp-btn-icon {
3583 | background-color: rgba(0, 0, 0, 0);
3584 | height: 100%
3585 | }
3586 |
3587 | .msp-plugin .pdbemolstar-state-gallery-title-box .pdbemolstar-state-gallery-title {
3588 | margin: 5px;
3589 | min-height: 2.9em;
3590 | display: flex;
3591 | flex-direction: row;
3592 | align-items: center;
3593 | text-align: center;
3594 | font-weight: bold
3595 | }
3596 |
3597 | .msp-plugin .pdbemolstar-state-gallery-title-box .pdbemolstar-state-gallery-title .pdbemolstar-state-gallery-title-icon {
3598 | width: 1.2em
3599 | }
3600 |
3601 | .msp-plugin .pdbemolstar-state-gallery-title-box .pdbemolstar-state-gallery-title .pdbemolstar-state-gallery-title-text {
3602 | margin-right: 1.2em;
3603 | padding-inline: 4px
3604 | }
--------------------------------------------------------------------------------