panel icon indicating copy to clipboard operation
panel copied to clipboard

pn.pane.HiPlot

Open avivazran opened this issue 1 year ago • 11 comments

I would benefit from having a native way to display HiPlot object in my application

given a hiplot experiment exp of type hiplot.experiment.Experiment: I would like to be able to wrap it inside a pn.pane.HiPlot(exp)

I have previously tried to use exp.to_html method to:

  1. use it with pn.pane.HTML - hiplot isn't rendering.
  2. save the pn.pane.HTML into a buffer by: pn.pane.HTML(exp.to_html()).save(BytesIO(), embed=True, resources=bokeh.resources.INLINE,embed_json=True)

avivazran avatar Jan 22 '24 13:01 avivazran

so is it something to that might happen? Because this can be an amazing component in my opinion. achieved something similar with Plotly pane but rendering stutters on big data frame

avivazran avatar Apr 11 '24 01:04 avivazran

Is this HiPlot? https://github.com/facebookresearch/hiplot

It seems like it's been archived?

What if you put it in iframe or try wrapping it in ReactiveHTML? https://panel.holoviz.org/explanation/components/reactive_html_components.html

ahuang11 avatar Apr 11 '24 19:04 ahuang11

Please note that even though HiPlot is quite popular the repository has just been "archived" by facebook.

image

There is no explanation why.

MarcSkovMadsen avatar Apr 12 '24 03:04 MarcSkovMadsen

I tried to see if I could make a quick prototype. I see the following challenges

  • HiPlot repository is now archived
  • There is no plain javascript bundle that can be used from cdn. But it can be built as described in https://facebookresearch.github.io/hiplot/contributing.html#building-javascript-bundle.
  • There is a javascript release though that we can try to use. When I use it I get exception. I get the same error when using release 0.1.33 instead of the latest release candidate.
import panel as pn
import param
from panel.reactive import ReactiveHTML

pn.extension()


class HiPlot(ReactiveHTML):
    index = param.Integer(default=0)
    __javascript__=[
            "https://unpkg.com/react@17/umd/react.development.js",
            "https://unpkg.com/react-dom@17/umd/react-dom.development.js",
            "https://unpkg.com/@babel/standalone/babel.min.js",
            "https://cdn.jsdelivr.net/npm/[email protected]/dist/hiplot.lib.min.js",
        ]

    _template = """
<div id="hiplotContainer" style="background-color: white"><div style="text-align: center">Loading HiPlot...</div>
    <noscript>
    HiPlot needs JavaScript to run
    </noscript>
</div>    
"""

    _scripts = {
        "render": """
console.log(hiplot)

var experimentData = [
    {'opt': 'sgd', 'lr': 0.01, 'dropout': 0.1},
    {'opt': 'adam', 'lr': 0.1, 'dropout': 0.2},
    {'opt': 'adam', 'lr': 1., 'dropout': 0.3},
    {'opt': 'sgd', 'lr': 0.001, 'dropout': 0.4},
];
var experiment = hiplot.Experiment.from_iterable(experimentData);
ReactDOM.render(
    React.createElement(
        hiplot.HiPlot, 
        {experiment: experiment}
    ), 
    hiplotContainer
)
"""
    }

HiPlot(width=500, height=200).servable()

image

See also https://facebookresearch.github.io/hiplot/tuto_javascript.html#

MarcSkovMadsen avatar Apr 12 '24 04:04 MarcSkovMadsen

I’ve also tried make a prototype using ReactiveHTML but got confused with the JavaScript. I think the best approach will be to build the component straight from the JavaScript (or react as it is used in the streamlit component) but I lack the required Js/react knowledge. When pip install hiplot - there are new js files in the site-packages/hiplot lib which are used when exporting the plot to html. Maybe we can leverage that?

I think that the fact it was archived is good because once we will have a working prototype it will be safe from breaking code changes.

I would like to explain my motivation for enabling this component. I am an electrical engineer working in the field of High speed networking. We are collecting huge amounts of data and we need to dig through it, looking for anomalies which might cause errors. my team is unique in the company and have knowledge in python. I’ve been experimenting with Panel for about a year now and I believe that this awesome package can bring a huge change to the teams working methodology. Hiplot has proved itself in helping us quickly visualize the state of the system and quickly find errors. My target is to have my company become a customer of Panel for fast dashboarding for our operations.

Having a hiplot component implemented in my proof of concept will undoubtedly seal the deal.

I know that it might be a lot of work to bring up this component but just so you know that there is a huge benefit in it for HW validation field which in my company, isn’t realized. And we are top in our field. Again, if someone could take this project and bring it up from the Js/Ts code it will open Panel to a whole new field. I am willing to help however Ican to make it happen. Even working closely with your engineers to share knowledge I already have when experimenting with the package and its structure.

please don’t discard this idea.

avivazran avatar Apr 17 '24 07:04 avivazran

The next version of Panel will ship ReactiveESM (or something similar). It should make it easier to create React components.

I tried using what is in the reactive_html_esm branch.

import param
import panel as pn

pn.extension()

class HiPlot(pn.ReactiveESM):
    value = param.List()

    _esm = """
import {default as hip} from "https://esm.sh/[email protected]";

console.log(hip);

function App(props) {
    const [value, setValue] = props.state.value;
    console.log(value)
    const experiment = hip.Experiment.from_iterable(value);
    return <hip.HiPlot experiment={experiment} />;
}

export function render({state}) {
    return <App state={state}/>;
}
"""

experiment = [
    {'opt': 'sgd', 'lr': 0.01, 'dropout': 0.1},
    {'opt': 'adam', 'lr': 0.1, 'dropout': 0.2},
    {'opt': 'adam', 'lr': 1., 'dropout': 0.3},
    {'opt': 'sgd', 'lr': 0.001, 'dropout': 0.4},
]
HiPlot(value=experiment).servable()

But for some reason it raises an exception. Whether its on HiPlot side or Panel side I don't know.

image

MarcSkovMadsen avatar May 10 '24 05:05 MarcSkovMadsen

Hi, @MarcSkovMadsen so I've managed to come up with some advancement.

the TypeError: Cannot read properties of undefined (reading 'fnOrder') is caused when "TABLE" is enabled in theenabled_displays. image

removing it actually loaded the parallel plot. image

however, we can see that the structure of the rendered html is off. my guess is that it is related either to the unknown div ID or the loading of the CSS files.

so far no ReactiveESM class in panel package so can't test it.

@ahuang11 I've managed to use IFrame to display the graph properly. image

the downside is that I any interaction done by the user with the plot is (changing columns order, coloring by column etc..) is not available to me. in other words, the state of the Hiplot object is not reachable, which is not the desired. I would like to be able to access the plot current state as I want to update the data live while maintaining the adjustments done by the user.

avivazran avatar Jun 10 '24 10:06 avivazran

I really feel like i'm on the brink of solving this. but I get this in the console:

image

I have a feeling that CSS handling is off and this is the last thing to solve before going into adding some logic. @MarcSkovMadsen can you maybe help here please?

from bokeh.io import output_notebook,push_notebook
import panel as pn
import param
from panel.reactive import ReactiveHTML


class HiPlot(ReactiveHTML):
    elem = param.Parameter()
    __javascript__=[
            "https://unpkg.com/react@17/umd/react.development.js",
            "https://unpkg.com/react-dom@17/umd/react-dom.development.js",
            "https://unpkg.com/@babel/standalone/babel.min.js",
            # "https://cdn.jsdelivr.net/npm/[email protected]/webpack.config.js"
             #"https://cdn.jsdelivr.net/npm/[email protected]/dist/hiplot.lib.min.js"
             "https://cdn.jsdelivr.net/npm/[email protected]/dist/hiplot.lib.js"
            # "https://cdn.jsdelivr.net/npm/[email protected]/dist/hiplot.lib.min.js",
            # "https://cdn.jsdelivr.net/npm/[email protected]/dist/hiplot.bundle.js.map",
        # "https://cdn.jsdelivr.net/npm/[email protected]/dist/hiplot.bundle.js.map",
        #"https://cdn.jsdelivr.net/npm/[email protected]/dist/hiplot.lib.js",
        
        ]

    _template = """
    
<div id="hiplotContainer" style="background-color: white"><div style="text-align: center">Loading HiPlot...</div>
    <noscript>
    HiPlot needs JavaScript to run
    </noscript>
</div>    
"""
    
    __css__=[
        "https://unpkg.com/bootstrap/dist/css/bootstrap.min.css",
        "https://cdn.jsdelivr.net/npm/[email protected]/src/parallel/parallel.scss"
       # "https://cdn.jsdelivr.net/npm/[email protected]/src/style/global.scss",
        #"https://cdn.jsdelivr.net/npm/[email protected]/src/style/bs-dark.scss",
        #"https://cdn.jsdelivr.net/npm/[email protected]/src/style/bs-light.scss",
        #"https://cdn.jsdelivr.net/npm/[email protected]/src/style/bs-light.scss",
        #"https://cdn.jsdelivr.net/npm/[email protected]/src/parallel/parallel.scss",
        
        

    ]
    _scripts = {
        "render": """

console.log(hiplot)

var experimentData = [
    {'opt': 'sgd', 'lr': 0.01, 'dropout': 0.1},
    {'opt': 'adam', 'lr': 0.1, 'dropout': 0.2},
    {'opt': 'adam', 'lr': 1., 'dropout': 0.3},
    {'opt': 'sgd', 'lr': 0.001, 'dropout': 0.4},
];
var experiment = hiplot.Experiment.from_iterable(experimentData);
let plugins = hiplot.createDefaultPlugins();
delete plugins[hiplot.DefaultPlugins.TABLE];
delete plugins[hiplot.DefaultPlugins.XY];
delete plugins[hiplot.DefaultPlugins.DISTRIBUTION];
console.log(experiment)
var elem = React.createElement(
        hiplot.HiPlot, 
        {experiment: experiment,plugins:plugins,dark:false}
        
    );

console.log('elem')
console.log(elem.props.plugins)

ReactDOM.render(elem, hiplotContainer);
"""
    }
pn.extension()
HiPlot(width=1000, height=1000).servable()

image

avivazran avatar Jun 21 '24 11:06 avivazran

Think it's related to the bootstrap the package is using. As it doesn't have access to the html inside the shadow dom. Is there a strategy to override this somehow or replace bootstrap with material or something like this?

avivazran avatar Jun 21 '24 12:06 avivazran

scss files need to be compiled and cannot be used directly in the browser to my knowledge. But if you look inside the file it looks like css.

Try including the css inside a <style> tag in your ReactiveHTML _template.

MarcSkovMadsen avatar Jun 21 '24 13:06 MarcSkovMadsen

Ahh. There are several scss files. Try to see if you can find some compiled css files to include. For example I guess the Jupyter widget uses compiled css files.

MarcSkovMadsen avatar Jun 21 '24 13:06 MarcSkovMadsen

from hiplot_style import hiplot_styleheets

class HiPlotComponent(ReactiveHTML):

    colorby = param.String()
    dark_mode = param.Boolean(default=False)
    values = param.List(default=hiplot_default_data)

    _template = """
    
<div id="hiplotContainer" style="background-color: white"><div style="text-align: center; height:100%;width:100%;">Loading HiPlot...</div>
    <noscript>
    HiPlot needs JavaScript to run
    </noscript>
</div>    
"""

    # _dom_events = {'text-input': ['change']}

    # By declaring an _extension_name the component should be loaded explicitly with pn.extension('material-components')
    # _extension_name = 'material-components'
    _scripts = {
        "render": """
console.log(Object);


var experimentData = data.values

var experiment = hiplot.Experiment.from_iterable(experimentData);
console.log(experiment);
experiment.display_data[hiplot.DefaultPlugins.PARALLEL_PLOT] = {
  'hide': ['uid', 'from_uid'],
};
experiment.colorby = data.colorby;
let plugins = hiplot.createDefaultPlugins();

delete plugins[hiplot.DefaultPlugins.XY];
delete plugins[hiplot.DefaultPlugins.DISTRIBUTION];
delete plugins[hiplot.DefaultPlugins.TABLE];

console.log(data.dark_mode);
// Create React elements without JSX
var hip_elem =     React.createElement(
        hiplot.HiPlot,
        { 
            experiment: experiment,
            plugins: plugins,
            dark: data.dark_mode
        }
    )

console.log(hip_elem);
var elem = React.createElement(
    React.StrictMode,
    null,
    hip_elem
);


console.log(elem);
console.log(state);

// Render the React element
ReactDOM.render(
    elem,
    hiplotContainer // Replace with your actual container ID
);

        """,
        "dark_mode": """self.render();"""
    }

    __javascript__ = [
        # 'https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js',
            "https://unpkg.com/react@17/umd/react.development.js",
            "https://unpkg.com/react-dom@17/umd/react-dom.development.js",
            "https://unpkg.com/@babel/standalone/babel.min.js",
        # "https://cdn.jsdelivr.net/npm/[email protected]/dist/hiplot.lib.min.js",
        # "https://cdnjs.cloudflare.com/ajax/libs/datatables.net-responsive-bs4/3.0.2/responsive.bootstrap4.js",
        "https://unpkg.com/[email protected]/dist/hiplot.lib.js"
        # "https://cdn.jsdelivr.net/npm/[email protected]/dist/hiplot.lib.js",
    ]

    __css__ = [
        # 'https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css'
        # "https://unpkg.com/bootstrap/dist/css/bootstrap.min.css",
    ]

    __javascript_modules__ = [
    ]

    _stylesheets = [
        hiplot_styleheets['1.33']
    ]
    # _dom_events = {'dark_mode': ['change']}


avivazran avatar Jul 08 '24 11:07 avivazran

on different .py file otherwise messes up notebook css




hiplot_styleheets = {

'1.33':"""
._32HeyC7HWVqvVsPvUXaOos svg {
    font-family: Ubuntu, Tahoma, Helvetica, sans-serif;
}

._32HeyC7HWVqvVsPvUXaOos canvas,
._32HeyC7HWVqvVsPvUXaOos svg {
    position: absolute;
    top: 0;
    left: 0;
}

._32HeyC7HWVqvVsPvUXaOos {
    position: relative;
}

._1BHre2Oe6TFO4hTuim52P4 rect._1GcXKZcqKY3u_RsHyssxts {
    fill: rgba(100, 100, 100, 0.15);
    stroke: #fff;
}

._1BHre2Oe6TFO4hTuim52P4:hover rect._1GcXKZcqKY3u_RsHyssxts {
    stroke: #222;
    stroke-dasharray: 5, 5;
}

._1BHre2Oe6TFO4hTuim52P4 rect._1GcXKZcqKY3u_RsHyssxts:hover {
    stroke-dasharray: none;
}

._2jwlhkQibLWzPLqgPawz-2 .label-name {
    transform-origin: bottom left;
}

._3jWoJZMEJsghgskeSu6y2r ._26LxRBU1xQqr-IZixZo4Yr {
    font-size: 16px;
    font-weight: bold;
}

._3jWoJZMEJsghgskeSu6y2r {
    cursor: move;
    font-size: 16px;
}

._3jWoJZMEJsghgskeSu6y2r text {
    fill: #111;
    text-anchor: right;
    font-size: 13px;
    text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}

._3jWoJZMEJsghgskeSu6y2r line,
._3jWoJZMEJsghgskeSu6y2r path {
    fill: none;
    stroke: #777;
    stroke-width: 1;
}

._3jWoJZMEJsghgskeSu6y2r .tick {
    width: 200px;
}

.hip_thm--dark ._1BHre2Oe6TFO4hTuim52P4 rect._1GcXKZcqKY3u_RsHyssxts {
    fill: rgba(100, 100, 100, 0.15);
    stroke: #ddd;
}

.hip_thm--dark ._3jWoJZMEJsghgskeSu6y2r text {
    fill: #f2f2f2;
    text-shadow: 0 1px 0 #000, 1px 0 0 #000, 0 -1px 0 #000, -1px 0 0 #000;
}

.hip_thm--dark ._3jWoJZMEJsghgskeSu6y2r line,
.hip_thm--dark ._3jWoJZMEJsghgskeSu6y2r path {
    stroke: #777;
}

._1ZUSF-tbSSCscSmTY4-BEX {
    margin: 0;
    width: 100%;
    height: 100%;
    padding: 0;
}

._1ZUSF-tbSSCscSmTY4-BEX {
    font-family: Ubuntu, Tahoma, Helvetica, sans-serif;
}

.hip_thm--light {
    background: #f7f7f7;
    color: #404040;
}

._1ZUSF-tbSSCscSmTY4-BEX a {
    text-decoration: none;
}

._1AfhwbHQacWQLj0k2eqYPY {
    padding: 0 3.5%;
}

._3ffmvq_BKNEwt5UY9Gc-3z rect {
    fill: none;
}

._1ThJRgZ_-ADTF9DxMm1vO3 {
    fill: none;
}

.Z9LpR6Ss9OVkeNf1RPX89 {
    white-space: nowrap;
}

._3jBnCXb7Wq6d6Nj5qU0tZj,
._3PwMN63swXgYs2V3NaqW4N,
._2fSQp8UpBsNXqy2qBvI5iU {
    float: left;
}

._3jBnCXb7Wq6d6Nj5qU0tZj {
    width: 23%;
    margin: 0 1%;
}

._3PwMN63swXgYs2V3NaqW4N {
    width: 31.3%;
    margin: 0 1%;
}

._2fSQp8UpBsNXqy2qBvI5iU {
    width: 48%;
    margin: 0 1%;
}

._1ZUSF-tbSSCscSmTY4-BEX h3 {
    margin: 12px 0 9px;
}

._1ZUSF-tbSSCscSmTY4-BEX h3 small {
    color: #888;
    font-weight: normal;
}

._1ZUSF-tbSSCscSmTY4-BEX p {
    margin: 0.6em 0;
}

._1ZUSF-tbSSCscSmTY4-BEX small {
    line-height: 1.2em;
}

._3Xzbpi7clTMlgQuSC3TkSJ,
._3jHLkrE3pIpw8R_Qw5_T0S {
    width: 0%;
    font-weight: bold;
    height: 100%;
}

._3Xzbpi7clTMlgQuSC3TkSJ {
    background: #3d9aff;
    border-right: 1px solid #666;
}

._3jHLkrE3pIpw8R_Qw5_T0S {
    background: rgba(171, 171, 171, 0.5);
    border-right: 1px solid #999;
}

._1Mg1NRi7I_xMRF7P6dMc27 {
    height: 2px;
    line-height: 2px;
    width: 100%;
}

._2ZYBXU2M0MFXTysMcWHNaP {
    width: 268px;
    float: left;
}

.-wfDlWaWyKfeKj7tWu_NX {
    float: right;
    height: 24px;
    line-height: 24px;
}

._27zXkTqskweooVZri-rve2 button {
    border-color: #000 !important;
}

._27zXkTqskweooVZri-rve2 button:disabled {
    border: solid 1px transparent !important;
}

::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: #ddd;
    border-radius: 12px;
}

::-webkit-scrollbar-thumb {
    background: #b5b5b5;
    border-radius: 12px;
}

._1Bsj-IoFR-3UZmBWt4DK3U .tick line {
    color: #9a9a9a26;
}

._2TQYADEAYb-yeOW_o05tV6 .tick line {
    color: #9a9a9a26;
}

.wYt95QU-sFBMT3IkH8bwU {
    min-height: 100vh;
}

.mRwqXRNbsMNf1DyEbEt7a {
    overflow-x: auto;
}

._37gV_F_HNGkKc8oXAGsUwm {
    height: 10px;
    width: 10px;
    display: inline-block;
}

._2dARw8OX_2i77zjmu1zT9T {
    display: inline-block;
    width: 0px;
}

.QfFdce7wIRJIhiVuXhuDW {
    top: 100%;
    left: 0%;
}

._2dARw8OX_2i77zjmu1zT9T ._32FheJCQOHwmFVYLzJnNY6 {
    visibility: hidden;
    background-color: #000;
    color: #fff;
    text-align: center;
    padding: 5px;
    border-radius: 6px;
}

.Z9LpR6Ss9OVkeNf1RPX89:hover + ._32FheJCQOHwmFVYLzJnNY6 {
    visibility: visible;
}

.Z9LpR6Ss9OVkeNf1RPX89 {
    color: #5e5e5e;
}

.Z9LpR6Ss9OVkeNf1RPX89:hover {
    color: #000;
    text-decoration: underline dotted;
}

.hip_thm--dark .Z9LpR6Ss9OVkeNf1RPX89 {
    color: #b7b7

b7;
}

.hip_thm--dark .Z9LpR6Ss9OVkeNf1RPX89:hover {
    color: #fff;
}

.ZS2MuDjd27slndFC6jn1o line {
    stroke: #000;
    stroke-width: 2;
}

._3kwumxQ6vpcMAxOpEckXUT rect {
    fill: #9467bd;
}

._2jYuBVDUw-byb6rjf5UDty {
    width: 100%;
    height: 50px;
    font-family: monospace;
    font-size: 12pt;
    resize: none;
    overflow: hidden;
}

._1084BZwPDL5dGuJyHsG-Kb {
    height: 25px !important;
}

._27zXkTqskweooVZri-rve2 {
    border-bottom: 1px solid rgba(100, 100, 100, 0.35);
    background: #e2e2e2;
    padding: 6px 24px 4px;
    line-height: 24px;
}

._27zXkTqskweooVZri-rve2 h1 {
    display: inline-block;
    margin: 0px 14px 0 0;
}

._27zXkTqskweooVZri-rve2 button {
    vertical-align: top;
}

._1kTtwhJnygQhCtR86nYUmI {
    margin-left: 5px;
    margin-right: 5px;
}

.hip_thm--dark ._3jWoJZMEJsghgskeSu6y2r text._2MRseUYIh66-VQsHtH05Kh {
    fill: #ddd;
}

.hip_thm--dark ._27zXkTqskweooVZri-rve2 {
    background: #040404;
    color: #f3f3f3;
}

.hip_thm--dark {
    background: #131313;
    color: #e3e3e3;
}

.hip_thm--dark a {
    color: #5ae;
}

.hip_thm--dark ._1ThJRgZ_-ADTF9DxMm1vO3 {
    fill: none;
}

.hip_thm--dark ::-webkit-scrollbar-track {
    background: #222;
}

.hip_thm--dark ::-webkit-scrollbar-thumb {
    background: #444;
}

.hip_thm--dark ._27zXkTqskweooVZri-rve2 button:enabled {
    border-color: #fff !important;
}

.hip_thm--dark .ZS2MuDjd27slndFC6jn1o line {
    stroke: #fff;
    stroke-width: 2;
}

.hip_thm--dark ._3kwumxQ6vpcMAxOpEckXUT rect {
    fill: #635075;
}
""",

'1.32':"""
._32HeyC7HWVqvVsPvUXaOos svg {
  font-family: Ubuntu, Tahoma, Helvetica, sans-serif;
}

._32HeyC7HWVqvVsPvUXaOos canvas,
._32HeyC7HWVqvVsPvUXaOos svg {
  position: absolute;
  top: 0;
  left: 0;
}

._32HeyC7HWVqvVsPvUXaOos {
  position: relative;
}

._1BHre2Oe6TFO4hTuim52P4 rect._1GcXKZcqKY3u_RsHyssxts {
  fill: rgba(100, 100, 100, 0.15);
  stroke: #fff;
}

._1BHre2Oe6TFO4hTuim52P4:hover rect._1GcXKZcqKY3u_RsHyssxts {
  stroke: #222;
  stroke-dasharray: 5, 5;
}

._1BHre2Oe6TFO4hTuim52P4 rect._1GcXKZcqKY3u_RsHyssxts:hover {
  stroke-dasharray: none;
}

._2jwlhkQibLWzPLqgPawz-2 .label-name {
  transform-origin: bottom left;
}

._3jWoJZMEJsghgskeSu6y2r ._26LxRBU1xQqr-IZixZo4Yr {
  font-size: 16px;
  font-weight: bold;
}

._3jWoJZMEJsghgskeSu6y2r {
  cursor: move;
  font-size: 16px;
}

._3jWoJZMEJsghgskeSu6y2r text {
  fill: #111;
  text-anchor: right;
  font-size: 13px;
  text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}

._3jWoJZMEJsghgskeSu6y2r line,
._3jWoJZMEJsghgskeSu6y2r path {
  fill: none;
  stroke: #777;
  stroke-width: 1;
}

._3jWoJZMEJsghgskeSu6y2r .tick {
  width: 200px;
}

/* Dark theme */

.hip_thm--dark ._1BHre2Oe6TFO4hTuim52P4 rect._1GcXKZcqKY3u_RsHyssxts {
  fill: rgba(100, 100, 100, 0.15);
  stroke: #ddd;
}

.hip_thm--dark ._3jWoJZMEJsghgskeSu6y2r text {
  fill: #f2f2f2;
  text-shadow: 0 1px 0 #000, 1px 0 0 #000, 0 -1px 0 #000, -1px 0 0 #000;
}

.hip_thm--dark ._3jWoJZMEJsghgskeSu6y2r line,
.hip_thm--dark ._3jWoJZMEJsghgskeSu6y2r path {
  stroke: #777;
}

/* General styling */

._1ZUSF-tbSSCscSmTY4-BEX {
  margin: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  font-family: Ubuntu, Tahoma, Helvetica, sans-serif;
}

.hip_thm--light {
  background: #f7f7f7;
  color: #404040;
}

._1ZUSF-tbSSCscSmTY4-BEX a {
  text-decoration: none;
}

._1AfhwbHQacWQLj0k2eqYPY {
  padding: 0 3.5%;
}

._3ffmvq_BKNEwt5UY9Gc-3z rect {
  fill: none;
}

._1ThJRgZ_-ADTF9DxMm1vO3 {
  fill: none;
}

.Z9LpR6Ss9OVkeNf1RPX89 {
  white-space: nowrap;
}

._3jBnCXb7Wq6d6Nj5qU0tZj,
._3PwMN63swXgYs2V3NaqW4N,
._2fSQp8UpBsNXqy2qBvI5iU {
  float: left;
}

._3jBnCXb7Wq6d6Nj5qU0tZj {
  width: 23%;
  margin: 0 1%;
}

._3PwMN63swXgYs2V3NaqW4N {
  width: 31.3%;
  margin: 0 1%;
}

._2fSQp8UpBsNXqy2qBvI5iU {
  width: 48%;
  margin: 0 1%;
}

._1ZUSF-tbSSCscSmTY4-BEX h3 {
  margin: 12px 0 9px;
}

._1ZUSF-tbSSCscSmTY4-BEX h3 small {
  color: #888;
  font-weight: normal;
}

._1ZUSF-tbSSCscSmTY4-BEX p {
  margin: 0.6em 0;
}

._1ZUSF-tbSSCscSmTY4-BEX small {
  line-height: 1.2em;
}

._3Xzbpi7clTMlgQuSC3TkSJ,
._3jHLkrE3pIpw8R_Qw5_T0S {
  width: 0%;
  font-weight: bold;
  height: 100%;
}

._3Xzbpi7clTMlgQuSC3TkSJ {
  background: #3d9aff;
  border-right: 1px solid #666;
}

._3jHLkrE3pIpw8R_Qw5_T0S {
  background: rgba(171, 171, 171, 0.5);
  border-right: 1px solid #999;
}

._1Mg1NRi7I_xMRF7P6dMc27 {
  height: 2px;
  line-height: 2px;
  width: 100%;
}

._2ZYBXU2M0MFXTysMcWHNaP {
  width: 268px;
  float: left;
}

.-wfDlWaWyKfeKj7tWu_NX {
  float: right;
  height: 24px;
  line-height: 24px;
}

._27zXkTqskweooVZri-rve2 button {
  border-color: #000 !important;
}

._27zXkTqskweooVZri-rve2 button:disabled {
  border: solid 1px transparent !important;
}



._1Bsj-IoFR-3UZmBWt4DK3U .tick line {
  color: #9a9a9a26;
}

._2TQYADEAYb-yeOW_o05tV6 .tick line {
  color: #9a9a9a26;
}

.wYt95QU-sFBMT3IkH8bwU {
  min-height: 100vh;
}

.mRwqXRNbsMNf1DyEbEt7a {
  overflow-x: auto;
}

._37gV_F_HNGkKc8oXAGsUwm {
  height: 10px;
  width: 10px;
  display: inline-block;
}

._2dARw8OX_2i77zjmu1zT9T {
  display: inline-block;
  width: 0px;
}

.QfFdce7wIRJIhiVuXhuDW {
  top: 100%;
  left: 0%;
}

._2dARw8OX_2i77zjmu1zT9T ._32FheJCQOHwmFVYLzJnNY6 {
  visibility: hidden;
  background-color: #000;
  color: #fff;
  text-align: center;
  padding: 5px;
  border-radius: 6px;
}

/* Show the tooltip text when you mouse over the tooltip container */

.Z9LpR6Ss9OVkeNf1RPX89:hover + ._32FheJCQOHwmFVYLzJnNY6 {
  visibility: visible;
}

.Z9LpR6Ss9OVkeNf1RPX89 {
  color: #5e5e5e;
}

.Z9LpR6Ss9OVkeNf1RPX89:hover {
  color: #000;
  text-decoration: underline dotted;
}

/* Dark theme adjustments */

.hip_thm--dark .Z9LpR6Ss9OVkeNf1RPX89 {
  color: #b7b7b7;
}

.hip_thm--dark .Z9LpR6Ss9OVkeNf1RPX89:hover {
  color: #fff;
}

.ZS2MuDjd27slndFC6jn1o line {
  stroke: #000;
  stroke-width: 2;
}

._3kwumxQ6vpcMAxOpEckXUT rect {
  fill: #9467bd;
}

._2jYuBVDUw-byb6rjf5UDty {
  width: 100%;
  height: 50px;
  font-family: monospace;
  font-size: 12pt;
  resize: none;
  overflow: hidden;
}

._1084BZwPDL5dGuJyHsG-Kb {
  height: 25px !important;
}

._27zXkTqskweooVZri-rve2 {
  border-bottom: 1px solid rgba(100, 100, 100, 0.35);
  background: #e2e2e2;
  padding: 6px 24px 4px;
  line-height: 24px;
}

._27zXkTqskweooVZri-rve2 h1 {
  display: inline-block;
  margin: 0px 14px 0 0;
}

._27zXkTqskweooVZri-rve2 button {
  vertical-align: top;
}

._1kTtwhJnygQhCtR86nYUmI {
  margin-left: 5px;
  margin-right: 5px;
}

.hip_thm--dark ._3jWoJZMEJsghgskeSu6y2r text._2MRseUYIh66-VQsHtH05Kh {
  fill: #ddd;
}

.hip_thm--dark ._27zXkTqskweooVZri-rve2 {
  background: #040404;
  color: #f3f3f3;
}

.hip_thm--dark {
  background: #131313;
  color: #e3e3e3;
}

.hip_thm--dark a {
  color: #5ae;
}

.hip_thm--dark ._1ThJRgZ_-ADTF9DxMm1vO3 {
  fill: none;
}

.hip_thm--dark ::-webkit-scrollbar-track {
  background: #222;
}

.hip_thm--dark ::-webkit-scrollbar-thumb {
  background: #444;
}

.hip_thm--dark ._27zXkTqskweooVZri-rve2 button:enabled {
  border-color: #fff !important;
}

.hip_thm--dark .ZS2MuDjd27slndFC6jn1o line {
  stroke: #fff;
  stroke-width: 2;
}

.hip_thm--dark ._3kwumxQ6vpcMAxOpEckXUT rect {
  fill: #635075;
}
""",

}

avivazran avatar Jul 08 '24 13:07 avivazran