Spaces:
Sleeping
Sleeping
QAway-to
commited on
Commit
·
e55ebde
1
Parent(s):
6e1af65
Revert "New tabs and functions v4.5"
Browse filesThis reverts commit 978bdd6a
- app.py +10 -0
- core/crypto_dashboard.py +41 -43
- core/multi_charts.py +56 -0
- core/styles/crypto_dashboard.css +17 -19
app.py
CHANGED
|
@@ -100,6 +100,16 @@ with gr.Blocks(css=base_css) as demo:
|
|
| 100 |
# первичная инициализация при запуске
|
| 101 |
demo.load(fn=update_visuals, inputs=None, outputs=[price_plot, vol_plot], _js="() => 'Bitcoin vs Ethereum'")
|
| 102 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
# --- Crypto Intelligence Dashboard (Plotly Edition + KPI line) ---
|
| 104 |
with gr.TabItem("Crypto Intelligence Dashboard"):
|
| 105 |
gr.HTML(f"<style>{crypto_css}</style>")
|
|
|
|
| 100 |
# первичная инициализация при запуске
|
| 101 |
demo.load(fn=update_visuals, inputs=None, outputs=[price_plot, vol_plot], _js="() => 'Bitcoin vs Ethereum'")
|
| 102 |
|
| 103 |
+
<<<<<<< HEAD
|
| 104 |
+
=======
|
| 105 |
+
|
| 106 |
+
# первичная инициализация при запуске (Gradio 5.x compatible)
|
| 107 |
+
def init_visuals():
|
| 108 |
+
return update_visuals("Bitcoin vs Ethereum")
|
| 109 |
+
|
| 110 |
+
demo.load(fn=init_visuals, inputs=None, outputs=[price_plot, vol_plot])
|
| 111 |
+
|
| 112 |
+
>>>>>>> parent of 53d7057 (Change UI v1.0)
|
| 113 |
# --- Crypto Intelligence Dashboard (Plotly Edition + KPI line) ---
|
| 114 |
with gr.TabItem("Crypto Intelligence Dashboard"):
|
| 115 |
gr.HTML(f"<style>{crypto_css}</style>")
|
core/crypto_dashboard.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
| 1 |
"""
|
| 2 |
-
Crypto Dashboard — Plotly Edition
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
KPI-строка компактная: BTC, ETH, SOL, DOGE.
|
| 7 |
"""
|
| 8 |
import requests
|
| 9 |
import pandas as pd
|
|
@@ -11,19 +10,18 @@ import plotly.express as px
|
|
| 11 |
from services.llm_client import llm_service
|
| 12 |
|
| 13 |
|
| 14 |
-
def
|
| 15 |
url = "https://api.coinlore.net/api/tickers/"
|
| 16 |
data = requests.get(url).json()["data"]
|
| 17 |
df = pd.DataFrame(data)
|
| 18 |
-
for col in [
|
| 19 |
-
|
| 20 |
-
"percent_change_1h", "percent_change_24h", "percent_change_7d"
|
| 21 |
-
]:
|
| 22 |
df[col] = pd.to_numeric(df[col], errors="coerce")
|
| 23 |
return df.head(limit)
|
| 24 |
|
| 25 |
|
| 26 |
-
def _kpi_line(df
|
|
|
|
| 27 |
tracked = ["BTC", "ETH", "SOL", "DOGE"]
|
| 28 |
parts = []
|
| 29 |
for sym in tracked:
|
|
@@ -35,34 +33,16 @@ def _kpi_line(df: pd.DataFrame) -> str:
|
|
| 35 |
arrow = "↑" if ch > 0 else "↓"
|
| 36 |
color = "#4ade80" if ch > 0 else "#f87171"
|
| 37 |
parts.append(
|
| 38 |
-
f"<
|
| 39 |
-
f"<span style='color:{color}'>{arrow} {abs(ch):.2f}%</span
|
| 40 |
)
|
| 41 |
return " , ".join(parts)
|
| 42 |
|
| 43 |
|
| 44 |
-
def
|
| 45 |
-
|
| 46 |
-
laggards = df.sort_values("percent_change_24h").head(3)["symbol"].tolist()
|
| 47 |
-
prompt = f"""
|
| 48 |
-
Summarize today's crypto market based on Coinlore data.
|
| 49 |
-
Top gainers: {', '.join(leaders)}.
|
| 50 |
-
Top losers: {', '.join(laggards)}.
|
| 51 |
-
Include: overall sentiment, volatility/liquidity, short-term outlook.
|
| 52 |
-
"""
|
| 53 |
-
text = ""
|
| 54 |
-
for delta in llm_service.stream_chat(
|
| 55 |
-
messages=[{"role": "user", "content": prompt}],
|
| 56 |
-
model="meta-llama/Meta-Llama-3.1-8B-Instruct",
|
| 57 |
-
):
|
| 58 |
-
text += delta
|
| 59 |
-
return text
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
def build_crypto_dashboard(top_n: int = 50):
|
| 63 |
-
df = _fetch_coinlore(top_n)
|
| 64 |
|
| 65 |
-
#
|
| 66 |
fig_treemap = px.treemap(
|
| 67 |
df,
|
| 68 |
path=["symbol"],
|
|
@@ -74,14 +54,13 @@ def build_crypto_dashboard(top_n: int = 50):
|
|
| 74 |
fig_treemap.update_layout(
|
| 75 |
title=None,
|
| 76 |
template="plotly_dark",
|
| 77 |
-
coloraxis_colorbar=dict(title=None),
|
| 78 |
-
autosize=True,
|
| 79 |
margin=dict(l=5, r=5, t=5, b=5),
|
| 80 |
paper_bgcolor="rgba(0,0,0,0)",
|
| 81 |
plot_bgcolor="rgba(0,0,0,0)",
|
| 82 |
)
|
| 83 |
|
| 84 |
-
#
|
| 85 |
top = df.sort_values("percent_change_24h", ascending=False).head(12)
|
| 86 |
fig_bar = px.bar(
|
| 87 |
top,
|
|
@@ -95,14 +74,13 @@ def build_crypto_dashboard(top_n: int = 50):
|
|
| 95 |
fig_bar.update_layout(
|
| 96 |
title=None,
|
| 97 |
template="plotly_dark",
|
| 98 |
-
coloraxis_colorbar=dict(title=None),
|
| 99 |
-
autosize=True,
|
| 100 |
margin=dict(l=40, r=10, t=5, b=18),
|
| 101 |
paper_bgcolor="rgba(0,0,0,0)",
|
| 102 |
plot_bgcolor="rgba(0,0,0,0)",
|
| 103 |
)
|
| 104 |
|
| 105 |
-
#
|
| 106 |
fig_bubble = px.scatter(
|
| 107 |
df.head(60),
|
| 108 |
x="market_cap_usd",
|
|
@@ -118,11 +96,31 @@ def build_crypto_dashboard(top_n: int = 50):
|
|
| 118 |
fig_bubble.update_layout(
|
| 119 |
title=None,
|
| 120 |
template="plotly_dark",
|
| 121 |
-
coloraxis_colorbar=dict(title=None),
|
| 122 |
-
autosize=True,
|
| 123 |
margin=dict(l=36, r=10, t=5, b=18),
|
| 124 |
paper_bgcolor="rgba(0,0,0,0)",
|
| 125 |
plot_bgcolor="rgba(0,0,0,0)",
|
| 126 |
)
|
| 127 |
|
| 128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""
|
| 2 |
+
Crypto Dashboard — Plotly Edition (clean layout)
|
| 3 |
+
• убраны colorbar заголовки (percent_change_*)
|
| 4 |
+
• уменьшены отступы KPI
|
| 5 |
+
• без глобального Markdown-заголовка
|
|
|
|
| 6 |
"""
|
| 7 |
import requests
|
| 8 |
import pandas as pd
|
|
|
|
| 10 |
from services.llm_client import llm_service
|
| 11 |
|
| 12 |
|
| 13 |
+
def fetch_coinlore_data(limit=100):
|
| 14 |
url = "https://api.coinlore.net/api/tickers/"
|
| 15 |
data = requests.get(url).json()["data"]
|
| 16 |
df = pd.DataFrame(data)
|
| 17 |
+
for col in ["price_usd", "market_cap_usd", "volume24",
|
| 18 |
+
"percent_change_1h", "percent_change_24h", "percent_change_7d"]:
|
|
|
|
|
|
|
| 19 |
df[col] = pd.to_numeric(df[col], errors="coerce")
|
| 20 |
return df.head(limit)
|
| 21 |
|
| 22 |
|
| 23 |
+
def _kpi_line(df) -> str:
|
| 24 |
+
"""Формирует компактную KPI-строку без лишних пробелов"""
|
| 25 |
tracked = ["BTC", "ETH", "SOL", "DOGE"]
|
| 26 |
parts = []
|
| 27 |
for sym in tracked:
|
|
|
|
| 33 |
arrow = "↑" if ch > 0 else "↓"
|
| 34 |
color = "#4ade80" if ch > 0 else "#f87171"
|
| 35 |
parts.append(
|
| 36 |
+
f"<b>{sym}</b> ${price:,.0f} "
|
| 37 |
+
f"<span style='color:{color}'>{arrow} {abs(ch):.2f}%</span>"
|
| 38 |
)
|
| 39 |
return " , ".join(parts)
|
| 40 |
|
| 41 |
|
| 42 |
+
def build_crypto_dashboard(top_n=50):
|
| 43 |
+
df = fetch_coinlore_data(top_n)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
+
# === Treemap ===
|
| 46 |
fig_treemap = px.treemap(
|
| 47 |
df,
|
| 48 |
path=["symbol"],
|
|
|
|
| 54 |
fig_treemap.update_layout(
|
| 55 |
title=None,
|
| 56 |
template="plotly_dark",
|
| 57 |
+
coloraxis_colorbar=dict(title=None), # 🔹 убираем надпись percent_change_24h
|
|
|
|
| 58 |
margin=dict(l=5, r=5, t=5, b=5),
|
| 59 |
paper_bgcolor="rgba(0,0,0,0)",
|
| 60 |
plot_bgcolor="rgba(0,0,0,0)",
|
| 61 |
)
|
| 62 |
|
| 63 |
+
# === Bar chart (Top gainers) ===
|
| 64 |
top = df.sort_values("percent_change_24h", ascending=False).head(12)
|
| 65 |
fig_bar = px.bar(
|
| 66 |
top,
|
|
|
|
| 74 |
fig_bar.update_layout(
|
| 75 |
title=None,
|
| 76 |
template="plotly_dark",
|
| 77 |
+
coloraxis_colorbar=dict(title=None), # 🔹 убираем надпись percent_change_24h
|
|
|
|
| 78 |
margin=dict(l=40, r=10, t=5, b=18),
|
| 79 |
paper_bgcolor="rgba(0,0,0,0)",
|
| 80 |
plot_bgcolor="rgba(0,0,0,0)",
|
| 81 |
)
|
| 82 |
|
| 83 |
+
# === Scatter (Market Cap vs Volume) ===
|
| 84 |
fig_bubble = px.scatter(
|
| 85 |
df.head(60),
|
| 86 |
x="market_cap_usd",
|
|
|
|
| 96 |
fig_bubble.update_layout(
|
| 97 |
title=None,
|
| 98 |
template="plotly_dark",
|
| 99 |
+
coloraxis_colorbar=dict(title=None), # 🔹 убираем надпись percent_change_7d
|
|
|
|
| 100 |
margin=dict(l=36, r=10, t=5, b=18),
|
| 101 |
paper_bgcolor="rgba(0,0,0,0)",
|
| 102 |
plot_bgcolor="rgba(0,0,0,0)",
|
| 103 |
)
|
| 104 |
|
| 105 |
+
# === LLM summary ===
|
| 106 |
+
summary = _ai_summary(df)
|
| 107 |
+
kpi_text = _kpi_line(df)
|
| 108 |
+
return fig_treemap, fig_bar, fig_bubble, summary, kpi_text
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def _ai_summary(df):
|
| 112 |
+
leaders = df.sort_values("percent_change_24h", ascending=False).head(3)["symbol"].tolist()
|
| 113 |
+
laggards = df.sort_values("percent_change_24h").head(3)["symbol"].tolist()
|
| 114 |
+
prompt = f"""
|
| 115 |
+
Summarize today's crypto market based on Coinlore data.
|
| 116 |
+
Top gainers: {', '.join(leaders)}.
|
| 117 |
+
Top losers: {', '.join(laggards)}.
|
| 118 |
+
Include: overall sentiment, volatility/liquidity, short-term outlook.
|
| 119 |
+
"""
|
| 120 |
+
text = ""
|
| 121 |
+
for delta in llm_service.stream_chat(
|
| 122 |
+
messages=[{"role": "user", "content": prompt}],
|
| 123 |
+
model="meta-llama/Meta-Llama-3.1-8B-Instruct",
|
| 124 |
+
):
|
| 125 |
+
text += delta
|
| 126 |
+
return text
|
core/multi_charts.py
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Multi Visualization Demo — ECharts + Highcharts (pure HTML/JS)
|
| 3 |
+
Никаких внешних питон-зависимостей, всё через CDN-скрипты.
|
| 4 |
+
"""
|
| 5 |
+
import json
|
| 6 |
+
import numpy as np
|
| 7 |
+
|
| 8 |
+
def build_echarts_html():
|
| 9 |
+
# сгенерируем 90 точек псевдо-цены
|
| 10 |
+
prices = (np.cumsum(np.random.randn(90)) * 100 + 42000).tolist()
|
| 11 |
+
xcats = list(range(1, 91))
|
| 12 |
+
return f"""
|
| 13 |
+
<div id="echarts_btc" style="height:360px;"></div>
|
| 14 |
+
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
|
| 15 |
+
<script>
|
| 16 |
+
var chart = echarts.init(document.getElementById('echarts_btc'), null, {{renderer:'canvas'}});
|
| 17 |
+
var option = {{
|
| 18 |
+
backgroundColor: '#0d1117',
|
| 19 |
+
title: {{ text: 'ECharts: BTC/USD (Simulated)', textStyle: {{ color:'#f0f6fc' }} }},
|
| 20 |
+
tooltip: {{ trigger: 'axis' }},
|
| 21 |
+
grid: {{ left: 30, right: 20, top: 40, bottom: 20 }},
|
| 22 |
+
xAxis: {{ type: 'category', data: {json.dumps(xcats)}, axisLine: {{ lineStyle: {{ color:'#30363d' }} }} }},
|
| 23 |
+
yAxis: {{ type: 'value', axisLine: {{ lineStyle: {{ color:'#30363d' }} }} }},
|
| 24 |
+
series: [{{
|
| 25 |
+
name: 'BTC/USD',
|
| 26 |
+
type: 'line',
|
| 27 |
+
smooth: true,
|
| 28 |
+
data: {json.dumps(prices)},
|
| 29 |
+
lineStyle: {{ width: 2, color: '#4f46e5' }},
|
| 30 |
+
areaStyle: {{ color: 'rgba(99,102,241,0.15)' }},
|
| 31 |
+
showSymbol: false
|
| 32 |
+
}}]
|
| 33 |
+
}};
|
| 34 |
+
chart.setOption(option);
|
| 35 |
+
window.addEventListener('resize', () => chart.resize());
|
| 36 |
+
</script>
|
| 37 |
+
"""
|
| 38 |
+
|
| 39 |
+
def build_highcharts_html():
|
| 40 |
+
prices = (np.cumsum(np.random.randn(90)) * 10 + 2300).tolist()
|
| 41 |
+
xcats = list(range(1, 91))
|
| 42 |
+
return f"""
|
| 43 |
+
<div id="highcharts_eth" style="height:360px;"></div>
|
| 44 |
+
<script src="https://code.highcharts.com/highcharts.js"></script>
|
| 45 |
+
<script>
|
| 46 |
+
Highcharts.chart('highcharts_eth', {{
|
| 47 |
+
chart: {{ backgroundColor: '#0d1117', style: {{ fontFamily: 'Inter' }} }},
|
| 48 |
+
title: {{ text: 'Highcharts: ETH/USD (Simulated)', style: {{ color: '#f0f6fc' }} }},
|
| 49 |
+
xAxis: {{ categories: {json.dumps(xcats)}, labels: {{ style: {{ color: '#9da5b4' }} }} }},
|
| 50 |
+
yAxis: {{ title: {{ text: 'Price (USD)', style: {{ color: '#9da5b4' }} }} }},
|
| 51 |
+
tooltip: {{ backgroundColor:'#161b22', borderColor:'#30363d', style: {{ color:'#f0f6fc' }} }},
|
| 52 |
+
credits: {{ enabled: false }},
|
| 53 |
+
series: [{{ name: 'ETH/USD', data: {json.dumps(prices)}, color: '#10b981' }}]
|
| 54 |
+
}});
|
| 55 |
+
</script>
|
| 56 |
+
"""
|
core/styles/crypto_dashboard.css
CHANGED
|
@@ -6,49 +6,43 @@
|
|
| 6 |
|
| 7 |
/* === KPI fixed line (строка с метриками) === */
|
| 8 |
#kpi_row {
|
| 9 |
-
margin: 0 !important;
|
|
|
|
| 10 |
padding: 0 !important;
|
| 11 |
-
height: auto !important;
|
| 12 |
}
|
| 13 |
#kpi_line {
|
| 14 |
width: 100%;
|
| 15 |
margin: 0 !important;
|
| 16 |
-
padding:
|
| 17 |
background: transparent;
|
| 18 |
color: #f0f6fc;
|
| 19 |
font-family: 'JetBrains Mono', monospace;
|
| 20 |
font-size: 14px;
|
| 21 |
-
line-height: 1.
|
| 22 |
white-space: nowrap;
|
| 23 |
overflow: hidden;
|
| 24 |
text-overflow: ellipsis;
|
| 25 |
display: flex;
|
| 26 |
align-items: center;
|
| 27 |
justify-content: flex-start;
|
|
|
|
| 28 |
border-bottom: 1px solid #30363d;
|
| 29 |
transition: all 0.2s ease-in-out;
|
| 30 |
-
min-height: unset !important;
|
| 31 |
-
height: auto !important;
|
| 32 |
}
|
|
|
|
|
|
|
| 33 |
.kpi-item {
|
| 34 |
margin-right: 14px;
|
| 35 |
letter-spacing: 0.2px;
|
| 36 |
transition: opacity 0.2s ease;
|
| 37 |
}
|
| 38 |
-
.kpi-item:hover {
|
| 39 |
-
|
| 40 |
-
/* === Убираем избыточные поля вокруг KPI и контролов === */
|
| 41 |
-
#kpi_row + .gr-row,
|
| 42 |
-
#controls_row {
|
| 43 |
-
margin-top: 0 !important;
|
| 44 |
-
padding-top: 0 !important;
|
| 45 |
-
margin-bottom: 0 !important;
|
| 46 |
-
padding-bottom: 0 !important;
|
| 47 |
}
|
| 48 |
|
| 49 |
-
/* === Controls (slider) — минимальные отступы === */
|
| 50 |
#controls_row {
|
| 51 |
-
margin-top:
|
| 52 |
margin-bottom: 2px !important;
|
| 53 |
padding: 0 !important;
|
| 54 |
}
|
|
@@ -72,9 +66,11 @@
|
|
| 72 |
background: transparent !important;
|
| 73 |
box-shadow: none !important;
|
| 74 |
}
|
| 75 |
-
[data-testid="plot-container"] svg {
|
|
|
|
|
|
|
| 76 |
|
| 77 |
-
/* === Скрываем только заголовки Plotly и подписи colorbar
|
| 78 |
.js-plotly-plot .plotly .gtitle,
|
| 79 |
.js-plotly-plot .plotly .g-xtitle,
|
| 80 |
.js-plotly-plot .plotly .g-ytitle,
|
|
@@ -84,6 +80,8 @@
|
|
| 84 |
display: none !important;
|
| 85 |
visibility: hidden !important;
|
| 86 |
}
|
|
|
|
|
|
|
| 87 |
.js-plotly-plot .legend text,
|
| 88 |
.js-plotly-plot .xtick text,
|
| 89 |
.js-plotly-plot .ytick text,
|
|
|
|
| 6 |
|
| 7 |
/* === KPI fixed line (строка с метриками) === */
|
| 8 |
#kpi_row {
|
| 9 |
+
margin-top: 0 !important;
|
| 10 |
+
margin-bottom: 0 !important;
|
| 11 |
padding: 0 !important;
|
|
|
|
| 12 |
}
|
| 13 |
#kpi_line {
|
| 14 |
width: 100%;
|
| 15 |
margin: 0 !important;
|
| 16 |
+
padding: 2px 6px !important;
|
| 17 |
background: transparent;
|
| 18 |
color: #f0f6fc;
|
| 19 |
font-family: 'JetBrains Mono', monospace;
|
| 20 |
font-size: 14px;
|
| 21 |
+
line-height: 1.2;
|
| 22 |
white-space: nowrap;
|
| 23 |
overflow: hidden;
|
| 24 |
text-overflow: ellipsis;
|
| 25 |
display: flex;
|
| 26 |
align-items: center;
|
| 27 |
justify-content: flex-start;
|
| 28 |
+
min-height: 24px;
|
| 29 |
border-bottom: 1px solid #30363d;
|
| 30 |
transition: all 0.2s ease-in-out;
|
|
|
|
|
|
|
| 31 |
}
|
| 32 |
+
|
| 33 |
+
/* KPI items */
|
| 34 |
.kpi-item {
|
| 35 |
margin-right: 14px;
|
| 36 |
letter-spacing: 0.2px;
|
| 37 |
transition: opacity 0.2s ease;
|
| 38 |
}
|
| 39 |
+
.kpi-item:hover {
|
| 40 |
+
opacity: 0.85;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
}
|
| 42 |
|
| 43 |
+
/* === Controls (slider + button) — минимальные отступы === */
|
| 44 |
#controls_row {
|
| 45 |
+
margin-top: 2px !important;
|
| 46 |
margin-bottom: 2px !important;
|
| 47 |
padding: 0 !important;
|
| 48 |
}
|
|
|
|
| 66 |
background: transparent !important;
|
| 67 |
box-shadow: none !important;
|
| 68 |
}
|
| 69 |
+
[data-testid="plot-container"] svg {
|
| 70 |
+
display: none !important;
|
| 71 |
+
}
|
| 72 |
|
| 73 |
+
/* === Скрываем только встроенные заголовки Plotly и подписи colorbar === */
|
| 74 |
.js-plotly-plot .plotly .gtitle,
|
| 75 |
.js-plotly-plot .plotly .g-xtitle,
|
| 76 |
.js-plotly-plot .plotly .g-ytitle,
|
|
|
|
| 80 |
display: none !important;
|
| 81 |
visibility: hidden !important;
|
| 82 |
}
|
| 83 |
+
|
| 84 |
+
/* === Явно оставляем видимыми подписи осей, тики, легенду и аннотации === */
|
| 85 |
.js-plotly-plot .legend text,
|
| 86 |
.js-plotly-plot .xtick text,
|
| 87 |
.js-plotly-plot .ytick text,
|