This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
================================================================================
|
||||
|
||||
【已完成】
|
||||
- V10.241 修正 `/metabase`、`/grist` 外部工具入口:全域導覽固定回 momo-pro 內部橋接頁,避免資料協作錯連 AwoooI;入口頁補路由狀態、設定診斷與可用替代分析入口,降低空白頁誤判。
|
||||
- V10.221 補 `/observability/ppt_audit_history` AiderHeal 背景任務可見性:正在修復中的簡報會顯示於產線頁,並提供 JSON 狀態端點讓派工後即時刷新,避免重新整理後不知道是否已在修。
|
||||
- V10.218 補 `/observability/ppt_audit_history` AiderHeal 去重鎖:同一份簡報已在背景修復時,再次點擊會回「已在執行中」,避免重複開 SSH / 模型 / git 修復流程。
|
||||
- V10.217 讓 `/observability/ppt_audit_history` 的 AiderHeal 派工改為非阻塞背景任務:頁面立即回「已排入」,修復工作在背景執行,避免瀏覽器與 Gunicorn worker 等 SSH、模型與 git push 到超時。
|
||||
|
||||
6
app.py
6
app.py
@@ -424,14 +424,12 @@ verify_metadata_tables()
|
||||
# ==========================================
|
||||
# 🔧 全域模板變數注入 (Context Processor)
|
||||
# ==========================================
|
||||
from config import METABASE_URL, GRIST_URL
|
||||
|
||||
@app.context_processor
|
||||
def inject_global_vars():
|
||||
"""注入全域變數到所有模板"""
|
||||
return {
|
||||
'metabase_url': METABASE_URL,
|
||||
'grist_url': GRIST_URL,
|
||||
'metabase_url': '/metabase',
|
||||
'grist_url': '/grist',
|
||||
'datetime_now': datetime.now(TAIPEI_TZ).strftime('%Y-%m-%d %H:%M:%S'),
|
||||
}
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.240"
|
||||
SYSTEM_VERSION = "V10.241"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
import os
|
||||
import zipfile
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from flask import Blueprint, Response, jsonify, render_template, send_from_directory, url_for
|
||||
from sqlalchemy import text
|
||||
|
||||
from auth import login_required
|
||||
from config import BASE_DIR, DATABASE_TYPE, SYSTEM_VERSION
|
||||
from config import BASE_DIR, DATABASE_TYPE, GRIST_URL, METABASE_URL, SYSTEM_VERSION
|
||||
from database.manager import DatabaseManager
|
||||
from database.models import Product, PriceRecord
|
||||
from services.json_storage import load_categories
|
||||
@@ -30,6 +31,81 @@ public_url = os.getenv('PUBLIC_URL', '服務啟動中...')
|
||||
STATIC_DIR = os.path.join(BASE_DIR, 'web/static')
|
||||
|
||||
|
||||
def _safe_launch_url(configured_url, bridge_path):
|
||||
"""Return a browser-safe launch URL, keeping nav links inside momo-pro by default."""
|
||||
candidate = (configured_url or '').strip()
|
||||
if not candidate or candidate.rstrip('/') == bridge_path.rstrip('/'):
|
||||
return ''
|
||||
if candidate.startswith('/') and not candidate.startswith('//'):
|
||||
return candidate
|
||||
|
||||
parsed = urlparse(candidate)
|
||||
if parsed.scheme in {'http', 'https'} and parsed.hostname == 'mo.wooo.work':
|
||||
if parsed.path.rstrip('/') == bridge_path.rstrip('/'):
|
||||
return ''
|
||||
return candidate
|
||||
return ''
|
||||
|
||||
|
||||
def _configured_url_label(configured_url):
|
||||
candidate = (configured_url or '').strip()
|
||||
if not candidate:
|
||||
return '未設定'
|
||||
parsed = urlparse(candidate)
|
||||
if parsed.scheme in {'http', 'https'} and parsed.hostname != 'mo.wooo.work':
|
||||
return f'{parsed.hostname}(已由入口攔截)'
|
||||
return candidate
|
||||
|
||||
|
||||
def _external_tool_payload(kind):
|
||||
if kind == 'metabase':
|
||||
launch_url = _safe_launch_url(METABASE_URL, '/metabase')
|
||||
return {
|
||||
'key': 'metabase',
|
||||
'eyebrow': 'Analytics Bridge',
|
||||
'title': '自訂圖表入口',
|
||||
'status_label': '待接上 BI 服務' if not launch_url else '可開啟',
|
||||
'summary': '這裡是 momo-pro 的 Metabase 安全入口。導覽先停在系統內部,不再出現空白頁或錯站外跳。',
|
||||
'detail': '目前正式主機沒有啟用 Metabase / Grist 的 bi profile 容器,Gateway 也沒有可用 proxy;因此先提供可用的分析替代入口與設定診斷。',
|
||||
'launch_href': launch_url,
|
||||
'launch_label': '開啟 Metabase',
|
||||
'configured_label': _configured_url_label(METABASE_URL),
|
||||
'checks': [
|
||||
{'label': '導覽路由', 'value': '/metabase', 'state': 'ok'},
|
||||
{'label': '跨站保護', 'value': '已鎖在 momo-pro', 'state': 'ok'},
|
||||
{'label': 'BI 服務', 'value': '尚未接入 proxy', 'state': 'warn'},
|
||||
],
|
||||
'actions': [
|
||||
{'label': '月份總表', 'href': '/monthly_summary_analysis', 'icon': 'fas fa-table'},
|
||||
{'label': '成長分析', 'href': '/growth_analysis', 'icon': 'fas fa-arrow-trend-up'},
|
||||
{'label': '當日業績', 'href': '/daily_sales', 'icon': 'fas fa-calendar-day'},
|
||||
],
|
||||
}
|
||||
|
||||
launch_url = _safe_launch_url(GRIST_URL, '/grist')
|
||||
return {
|
||||
'key': 'grist',
|
||||
'eyebrow': 'Data Collaboration',
|
||||
'title': '資料協作入口',
|
||||
'status_label': '錯鏈已攔截' if not launch_url else '可開啟',
|
||||
'summary': '資料協作入口已回到 momo-pro 內部,不會再連到 AwoooI 或其他專案站台。',
|
||||
'detail': 'Grist 的正式 momo-pro 專案隔離尚未接上 Gateway;在完成專屬服務前,本頁會提供資料作業入口與目前設定狀態。',
|
||||
'launch_href': launch_url,
|
||||
'launch_label': '開啟 Grist',
|
||||
'configured_label': _configured_url_label(GRIST_URL),
|
||||
'checks': [
|
||||
{'label': '導覽路由', 'value': '/grist', 'state': 'ok'},
|
||||
{'label': '錯站攔截', 'value': '已啟用', 'state': 'ok'},
|
||||
{'label': '協作服務', 'value': '尚未接入 proxy', 'state': 'warn'},
|
||||
],
|
||||
'actions': [
|
||||
{'label': '月份總表', 'href': '/monthly_summary_analysis', 'icon': 'fas fa-table'},
|
||||
{'label': '雲端匯入', 'href': '/auto_import', 'icon': 'fas fa-download'},
|
||||
{'label': '業績分析', 'href': '/sales_analysis', 'icon': 'fas fa-chart-bar'},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@system_public_bp.route('/favicon.ico')
|
||||
def favicon():
|
||||
"""使用既有品牌圖示回應瀏覽器預設 favicon 探測,避免全站 404 噪音。"""
|
||||
@@ -66,16 +142,7 @@ def metabase_status():
|
||||
'external_tool_status.html',
|
||||
active_page='metabase',
|
||||
system_version=SYSTEM_VERSION,
|
||||
tool={
|
||||
'key': 'metabase',
|
||||
'eyebrow': 'Analytics Bridge',
|
||||
'title': '自訂圖表入口',
|
||||
'status_label': '代理尚未接入',
|
||||
'summary': '正式入口已留在 momo-pro 內部,避免再落到 404 或空白頁。',
|
||||
'detail': 'Metabase 容器以 bi profile 管理;公開路由需由 Gateway / Nginx 接到 momo-metabase:3000 後才會切換為完整 BI 介面。',
|
||||
'primary_label': '回月份總表',
|
||||
'primary_href': '/monthly_summary_analysis',
|
||||
},
|
||||
tool=_external_tool_payload('metabase'),
|
||||
)
|
||||
|
||||
|
||||
@@ -88,16 +155,7 @@ def grist_status():
|
||||
'external_tool_status.html',
|
||||
active_page='grist',
|
||||
system_version=SYSTEM_VERSION,
|
||||
tool={
|
||||
'key': 'grist',
|
||||
'eyebrow': 'Data Collaboration',
|
||||
'title': '資料協作入口',
|
||||
'status_label': '錯鏈已攔截',
|
||||
'summary': '資料協作不再連到 grist.wooo.work,避免被轉往其他專案站台。',
|
||||
'detail': 'Grist 正式域名尚未完成 momo-pro 專案隔離;在完成 Gateway 綁定前,導覽會停在本頁狀態,不再跳出系統邊界。',
|
||||
'primary_label': '回月份總表',
|
||||
'primary_href': '/monthly_summary_analysis',
|
||||
},
|
||||
tool=_external_tool_payload('grist'),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
{% endif %}
|
||||
{% if metabase_url %}
|
||||
<a class="analysis-report-tab is-external {% if _analysis_active == 'metabase' %}is-active{% endif %}" href="{{ metabase_url }}">
|
||||
<i class="fas fa-chart-pie"></i>Metabase <i class="fas fa-up-right-from-square"></i>
|
||||
<i class="fas fa-chart-pie"></i>自訂圖表
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if grist_url %}
|
||||
<a class="analysis-report-tab is-external {% if _analysis_active == 'grist' %}is-active{% endif %}" href="{{ grist_url }}">
|
||||
<i class="fas fa-table"></i>Grist <i class="fas fa-up-right-from-square"></i>
|
||||
<i class="fas fa-table-cells"></i>資料協作
|
||||
</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
{% set _session_username = session.get('username') if session is defined else None %}
|
||||
{% set _session_role = session.get('role') if session is defined else None %}
|
||||
{% set _is_logged_in = session.get('logged_in') if session is defined else false %}
|
||||
{% set _analysis_pages = ['sales', 'daily_sales', 'monthly', 'growth'] %}
|
||||
{% set _analysis_pages = ['sales', 'daily_sales', 'monthly', 'growth', 'metabase', 'grist'] %}
|
||||
{% set _obs_pages = [
|
||||
'obs_overview', 'obs_agent_orchestration', 'obs_business_intel',
|
||||
'obs_host_health', 'obs_ai_calls', 'obs_budget',
|
||||
|
||||
@@ -202,14 +202,14 @@
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ metabase_url }}">
|
||||
<i class="fas fa-chart-pie me-2"></i>自訂圖表 (Metabase)
|
||||
<i class="fas fa-chart-pie me-2"></i>自訂圖表
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if grist_url %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ grist_url }}">
|
||||
<i class="fas fa-table me-2"></i>資料協作 (Grist)
|
||||
<i class="fas fa-table-cells me-2"></i>資料協作
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<section class="external-tool-hero">
|
||||
<div>
|
||||
<div class="external-tool-eyebrow">
|
||||
<i class="fas fa-link" aria-hidden="true"></i>
|
||||
<i class="fas fa-plug-circle-check" aria-hidden="true"></i>
|
||||
{{ tool.eyebrow }}
|
||||
</div>
|
||||
<h1>{{ tool.title }}</h1>
|
||||
@@ -19,15 +19,60 @@
|
||||
<span class="external-tool-status">{{ tool.status_label }}</span>
|
||||
</section>
|
||||
|
||||
<section class="external-tool-checks" aria-label="入口檢查">
|
||||
{% for check in tool.checks %}
|
||||
<article class="external-tool-check is-{{ check.state }}">
|
||||
<span>{{ check.label }}</span>
|
||||
<strong>{{ check.value }}</strong>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
<section class="external-tool-panel">
|
||||
<div class="external-tool-panel__body">
|
||||
<span class="external-tool-kicker">路由狀態</span>
|
||||
<h2>入口已由 momo-pro 接管</h2>
|
||||
<p>{{ tool.detail }}</p>
|
||||
<dl class="external-tool-diagnostics">
|
||||
<div>
|
||||
<dt>正式導覽</dt>
|
||||
<dd>/{{ tool.key }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>目前設定</dt>
|
||||
<dd>{{ tool.configured_label }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>版本</dt>
|
||||
<dd>{{ system_version }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="external-tool-panel__actions">
|
||||
{% if tool.launch_href %}
|
||||
<a class="btn btn-primary" href="{{ tool.launch_href }}">
|
||||
<i class="fas fa-arrow-up-right-from-square" aria-hidden="true"></i>{{ tool.launch_label }}
|
||||
</a>
|
||||
{% endif %}
|
||||
<a class="btn btn-outline-primary" href="/monthly_summary_analysis">
|
||||
<i class="fas fa-arrow-left" aria-hidden="true"></i>回分析報表
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="external-tool-actions" aria-label="可用替代入口">
|
||||
<div class="external-tool-section-title">
|
||||
<span class="external-tool-kicker">可用入口</span>
|
||||
<h2>先回到已上線的分析工作流</h2>
|
||||
</div>
|
||||
<div class="external-tool-action-grid">
|
||||
{% for action in tool.actions %}
|
||||
<a class="external-tool-action" href="{{ action.href }}">
|
||||
<i class="{{ action.icon }}" aria-hidden="true"></i>
|
||||
<span>{{ action.label }}</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<a class="btn btn-primary" href="{{ tool.primary_href }}">
|
||||
<i class="fas fa-arrow-left" aria-hidden="true"></i>{{ tool.primary_label }}
|
||||
</a>
|
||||
</section>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
44
tests/test_external_tool_entrypoints.py
Normal file
44
tests/test_external_tool_entrypoints.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def test_metabase_and_grist_navigation_stays_inside_momo_pro():
|
||||
app_source = (ROOT / "app.py").read_text(encoding="utf-8")
|
||||
shell = (ROOT / "templates/components/_ewoooc_shell.html").read_text(encoding="utf-8")
|
||||
tabs = (ROOT / "templates/components/_analysis_report_tabs.html").read_text(encoding="utf-8")
|
||||
legacy_nav = (ROOT / "templates/components/_navbar.html").read_text(encoding="utf-8")
|
||||
|
||||
assert "'metabase_url': '/metabase'" in app_source
|
||||
assert "'grist_url': '/grist'" in app_source
|
||||
assert "['sales', 'daily_sales', 'monthly', 'growth', 'metabase', 'grist']" in shell
|
||||
|
||||
combined = "\n".join([app_source, shell, tabs, legacy_nav])
|
||||
assert "grist.wooo.work" not in combined
|
||||
assert "awoooi" not in combined.lower()
|
||||
|
||||
|
||||
def test_external_tool_bridge_pages_are_diagnostic_not_blank():
|
||||
route_source = (ROOT / "routes/system_public_routes.py").read_text(encoding="utf-8")
|
||||
template = (ROOT / "templates/external_tool_status.html").read_text(encoding="utf-8")
|
||||
css = (ROOT / "web/static/css/page-external-tools.css").read_text(encoding="utf-8")
|
||||
|
||||
assert "def _external_tool_payload(kind)" in route_source
|
||||
assert "parsed.path.rstrip('/') == bridge_path.rstrip('/')" in route_source
|
||||
assert "external-tool-checks" in template
|
||||
assert "external-tool-diagnostics" in template
|
||||
assert "external-tool-action-grid" in template
|
||||
assert "尚未接入 proxy" in route_source
|
||||
assert "已由入口攔截" in route_source
|
||||
|
||||
assert "--momo-accent-rust" not in css
|
||||
assert "--momo-accent-honey" not in css
|
||||
|
||||
|
||||
def test_analysis_tabs_use_internal_tool_labels_without_external_icon():
|
||||
tabs = (ROOT / "templates/components/_analysis_report_tabs.html").read_text(encoding="utf-8")
|
||||
|
||||
assert "自訂圖表" in tabs
|
||||
assert "資料協作" in tabs
|
||||
assert "fa-up-right-from-square" not in tabs
|
||||
@@ -5,7 +5,8 @@
|
||||
}
|
||||
|
||||
.external-tool-hero,
|
||||
.external-tool-panel {
|
||||
.external-tool-panel,
|
||||
.external-tool-actions {
|
||||
background:
|
||||
radial-gradient(circle, rgba(45, 40, 32, 0.12) 1px, transparent 1.2px),
|
||||
var(--momo-bg-surface);
|
||||
@@ -20,7 +21,7 @@
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: var(--momo-space-5, 24px);
|
||||
padding: var(--momo-space-6, 32px);
|
||||
padding: var(--momo-space-5, 24px);
|
||||
}
|
||||
|
||||
.external-tool-eyebrow,
|
||||
@@ -28,7 +29,7 @@
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--momo-space-2, 8px);
|
||||
color: var(--momo-accent-rust);
|
||||
color: var(--momo-page-accent);
|
||||
font-size: var(--momo-text-body-sm);
|
||||
font-weight: var(--momo-font-weight-bold);
|
||||
letter-spacing: 0;
|
||||
@@ -44,11 +45,12 @@
|
||||
}
|
||||
|
||||
.external-tool-hero h1 {
|
||||
font-size: 48px;
|
||||
font-size: clamp(32px, 4vw, 48px);
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
.external-tool-panel h2 {
|
||||
font-size: var(--momo-text-title-lg);
|
||||
font-size: var(--momo-text-title, 17px);
|
||||
}
|
||||
|
||||
.external-tool-hero p,
|
||||
@@ -56,19 +58,62 @@
|
||||
max-width: 760px;
|
||||
margin: 0;
|
||||
color: var(--momo-text-secondary);
|
||||
font-size: var(--momo-text-body-lg);
|
||||
font-size: var(--momo-text-body, 14px);
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.external-tool-status {
|
||||
flex: 0 0 auto;
|
||||
border: 1px solid var(--momo-accent-rust);
|
||||
border: 1px solid var(--momo-page-accent);
|
||||
border-radius: var(--momo-radius-pill, 999px);
|
||||
color: var(--momo-accent-rust);
|
||||
color: var(--momo-page-accent);
|
||||
font-weight: var(--momo-font-weight-bold);
|
||||
padding: var(--momo-space-2, 8px) var(--momo-space-3, 12px);
|
||||
}
|
||||
|
||||
.external-tool-checks {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: var(--momo-space-3, 12px);
|
||||
}
|
||||
|
||||
.external-tool-check {
|
||||
min-height: 96px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
gap: var(--momo-space-3, 12px);
|
||||
padding: var(--momo-space-4, 16px);
|
||||
background: var(--momo-bg-surface);
|
||||
border: 1px solid var(--momo-border-subtle);
|
||||
border-left: 4px solid var(--momo-page-accent);
|
||||
border-radius: var(--momo-radius-lg, 8px);
|
||||
}
|
||||
|
||||
.external-tool-check.is-ok {
|
||||
border-left-color: var(--momo-success);
|
||||
}
|
||||
|
||||
.external-tool-check.is-warn {
|
||||
border-left-color: var(--momo-warning);
|
||||
}
|
||||
|
||||
.external-tool-check span,
|
||||
.external-tool-diagnostics dt {
|
||||
color: var(--momo-text-muted);
|
||||
font-size: var(--momo-text-body-sm);
|
||||
font-weight: var(--momo-font-weight-bold);
|
||||
}
|
||||
|
||||
.external-tool-check strong,
|
||||
.external-tool-diagnostics dd {
|
||||
margin: 0;
|
||||
color: var(--momo-text-primary);
|
||||
font-family: var(--momo-font-mono);
|
||||
font-size: var(--momo-text-body);
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.external-tool-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -77,13 +122,75 @@
|
||||
padding: var(--momo-space-5, 24px);
|
||||
}
|
||||
|
||||
.external-tool-panel .btn {
|
||||
.external-tool-diagnostics {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: var(--momo-space-3, 12px);
|
||||
margin: var(--momo-space-4, 16px) 0 0;
|
||||
}
|
||||
|
||||
.external-tool-diagnostics div {
|
||||
padding: var(--momo-space-3, 12px);
|
||||
background: rgba(255, 248, 238, 0.42);
|
||||
border: 1px solid var(--momo-border-subtle);
|
||||
border-radius: var(--momo-radius-md, 6px);
|
||||
}
|
||||
|
||||
.external-tool-panel__actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--momo-space-2, 8px);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.external-tool-panel .btn,
|
||||
.external-tool-action {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--momo-space-2, 8px);
|
||||
}
|
||||
|
||||
.external-tool-panel .btn {
|
||||
justify-content: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.external-tool-actions {
|
||||
padding: var(--momo-space-5, 24px);
|
||||
}
|
||||
|
||||
.external-tool-section-title h2 {
|
||||
margin: var(--momo-space-2, 8px) 0 0;
|
||||
color: var(--momo-text-primary);
|
||||
font-size: var(--momo-text-title);
|
||||
font-weight: var(--momo-font-weight-black);
|
||||
}
|
||||
|
||||
.external-tool-action-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: var(--momo-space-3, 12px);
|
||||
margin-top: var(--momo-space-4, 16px);
|
||||
}
|
||||
|
||||
.external-tool-action {
|
||||
min-height: 64px;
|
||||
padding: var(--momo-space-3, 12px);
|
||||
color: var(--momo-text-primary);
|
||||
background: var(--momo-bg-paper);
|
||||
border: 1px solid var(--momo-border-subtle);
|
||||
border-radius: var(--momo-radius-md, 6px);
|
||||
font-weight: var(--momo-font-weight-bold);
|
||||
text-decoration: none;
|
||||
transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
|
||||
}
|
||||
|
||||
.external-tool-action:hover {
|
||||
color: var(--momo-page-accent);
|
||||
background: var(--momo-bg-surface);
|
||||
border-color: var(--momo-border-strong);
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.external-tool-hero,
|
||||
.external-tool-panel {
|
||||
@@ -92,7 +199,17 @@
|
||||
padding: var(--momo-space-4, 16px);
|
||||
}
|
||||
|
||||
.external-tool-hero h1 {
|
||||
font-size: 32px;
|
||||
.external-tool-status {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.external-tool-checks,
|
||||
.external-tool-diagnostics,
|
||||
.external-tool-action-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.external-tool-panel__actions {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user