融合觀測台點陣視覺語彙
All checks were successful
CD Pipeline / deploy (push) Successful in 57s

This commit is contained in:
OoO
2026-05-13 18:51:46 +08:00
parent 5a21e2394e
commit f947469a36
4 changed files with 218 additions and 7 deletions

View File

@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.116"
SYSTEM_VERSION = "V10.117"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示

View File

@@ -3,7 +3,7 @@
* Rendered visual contract for AI observability pages.
*
* This complements the static template guard by checking computed CSS in a
* browser: typography, warm-token surfaces, radius, hero backgrounds, and
* browser: typography, warm-token surfaces, dot-matrix texture, radius, and
* mobile density across the 10 observability pages.
*/
@@ -195,6 +195,11 @@ function contrastRatio(fg, bg) {
return (lighter + 0.05) / (darker + 0.05);
}
function isDotMatrixBackground(backgroundImage) {
const value = String(backgroundImage || '');
return value.includes('radial-gradient') && !value.includes('linear-gradient');
}
async function collectMetrics(page) {
return page.evaluate(({ heroSelectors, titleSelectors, surfaceSelectors }) => {
const parseRgbValue = (value) => {
@@ -234,6 +239,7 @@ async function collectMetrics(page) {
const title = document.querySelector(titleSelectors);
const badRadius = [];
const badBackground = [];
const missingMatrix = [];
const badChipContrast = [];
for (const el of document.querySelectorAll(`.momo-observability-mode :is(${surfaceSelectors})`)) {
@@ -246,13 +252,24 @@ async function collectMetrics(page) {
text: String(el.textContent || '').trim().replace(/\s+/g, ' ').slice(0, 80),
});
}
if ((el.matches(heroSelectors) || el.matches('.obs-panel,.agent-panel,.biz-panel,.runtime-panel,.calls-panel,.gov-panel,.gate-panel,.rag-panel,.qa-panel,.quality-panel,.ppt-panel')) && style.backgroundImage !== 'none') {
const mustUseMatrix = el.matches(heroSelectors)
|| el.matches('.obs-panel,.agent-panel,.biz-panel,.runtime-panel,.calls-panel,.gov-panel,.gate-panel,.rag-panel,.qa-panel,.quality-panel,.ppt-panel')
|| el.matches('.obs-signal,.agent-signal,.biz-signal,.runtime-signal,.calls-signal,.gov-signal,.gate-signal,.rag-signal,.qa-signal,.quality-signal,.ppt-signal');
const hasRadialMatrix = style.backgroundImage.includes('radial-gradient');
const hasLegacyGradient = style.backgroundImage.includes('linear-gradient');
if (mustUseMatrix && (!hasRadialMatrix || hasLegacyGradient)) {
badBackground.push({
className: String(el.className || '').slice(0, 100),
backgroundImage: style.backgroundImage.slice(0, 100),
});
}
if (badRadius.length >= 8 && badBackground.length >= 8) break;
if (mustUseMatrix && !hasRadialMatrix) {
missingMatrix.push({
className: String(el.className || '').slice(0, 100),
text: String(el.textContent || '').trim().replace(/\s+/g, ' ').slice(0, 80),
});
}
if (badRadius.length >= 8 && badBackground.length >= 8 && missingMatrix.length >= 8) break;
}
for (const el of document.querySelectorAll('.momo-observability-mode :is(.badge,.obs-pill,[class$="-pill"],.biz-badge)')) {
@@ -292,6 +309,7 @@ async function collectMetrics(page) {
} : null,
badRadius,
badBackground,
missingMatrix,
badChipContrast,
};
}, { heroSelectors: HERO_SELECTORS, titleSelectors: TITLE_SELECTORS, surfaceSelectors: SURFACE_SELECTORS });
@@ -323,13 +341,16 @@ function issuesFor(metrics, viewport) {
issues.push('missing hero selector');
} else {
if (px(metrics.hero.radius) > 8.5) issues.push(`hero radius ${metrics.hero.radius}`);
if (metrics.hero.backgroundImage !== 'none') issues.push('hero background image is not none');
if (!isDotMatrixBackground(metrics.hero.backgroundImage)) {
issues.push('hero missing tokenized dot-matrix background');
}
if (metrics.hero.height > viewport.maxHeroHeight) {
issues.push(`hero too tall ${metrics.hero.height}px > ${viewport.maxHeroHeight}px`);
}
}
if (metrics.badRadius.length) issues.push(`surface radius offenders ${metrics.badRadius.length}`);
if (metrics.badBackground.length) issues.push(`surface background-image offenders ${metrics.badBackground.length}`);
if (metrics.badBackground.length) issues.push(`surface non-matrix background offenders ${metrics.badBackground.length}`);
if (metrics.missingMatrix.length) issues.push(`surface missing dot-matrix offenders ${metrics.missingMatrix.length}`);
if (metrics.badChipContrast.length) issues.push(`chip contrast offenders ${metrics.badChipContrast.length}`);
return issues;
}
@@ -385,7 +406,7 @@ async function main() {
const hero = result.metrics?.hero ? `${result.metrics.hero.height}px r=${result.metrics.hero.radius}` : 'n/a';
console.log(`${status} ${result.viewport} ${result.route} title=${title} hero=${hero}${result.issues.length ? ` issues=${result.issues.join('; ')}` : ''}`);
if (!result.passed && result.metrics) {
for (const bucket of ['badRadius', 'badBackground', 'badChipContrast']) {
for (const bucket of ['badRadius', 'badBackground', 'missingMatrix', 'badChipContrast']) {
for (const offender of result.metrics[bucket] || []) {
console.log(` ${bucket}: ${JSON.stringify(offender)}`);
}

View File

@@ -1761,6 +1761,101 @@
}
}
/* v3.8 dot-matrix integration: surfaces carry the product texture without legacy gradients. */
.momo-observability-mode {
--obs-matrix-dot: radial-gradient(color-mix(in srgb, var(--obs-accent) 14%, transparent) 0.85px, transparent 0.95px);
--obs-matrix-dot-soft: radial-gradient(color-mix(in srgb, var(--obs-muted) 12%, transparent) 0.75px, transparent 0.9px);
--obs-matrix-size: 13px 13px;
}
.momo-observability-mode :is(
.obs-hero,
.agent-hero,
.biz-command,
.runtime-hero,
.calls-hero,
.gov-hero,
.gate-hero,
.rag-hero,
.qa-hero,
.quality-hero,
.ppt-hero,
.obs-panel,
.agent-panel,
.biz-panel,
.runtime-panel,
.calls-panel,
.gov-panel,
.gate-panel,
.rag-panel,
.qa-panel,
.quality-panel,
.ppt-panel,
.obs-signal,
.agent-signal,
.biz-signal,
.runtime-signal,
.calls-signal,
.gov-signal,
.gate-signal,
.rag-signal,
.qa-signal,
.quality-signal,
.ppt-signal,
.biz-filter-card,
.biz-alert-strip,
.biz-strategy-card,
.biz-mini-metric,
.biz-decision-card,
.episode-card,
.similar-box,
.fix-card,
.root-card,
.rec-card,
.agent-card,
.caller-card,
.host-lane,
.strategy-card,
.obs-route-card
) {
background-color: var(--momo-bg-surface, #faf6ec) !important;
background-image: var(--obs-matrix-dot) !important;
background-position: 0 0 !important;
background-size: var(--obs-matrix-size) !important;
}
.momo-observability-mode :is(
.obs-panel,
.agent-panel,
.biz-panel,
.runtime-panel,
.calls-panel,
.gov-panel,
.gate-panel,
.rag-panel,
.qa-panel,
.quality-panel,
.ppt-panel,
.biz-filter-card,
.biz-strategy-card,
.episode-card,
.host-lane,
.caller-card,
.agent-card,
.rec-card,
.fix-card,
.root-card,
.strategy-card
) {
background-image: var(--obs-matrix-dot-soft) !important;
}
.momo-observability-mode :is(.obs-pill, [class$="-pill"], .biz-badge, .badge) {
background-image: var(--obs-matrix-dot-soft) !important;
background-position: 0 0 !important;
background-size: 10px 10px !important;
}
/* v3.7 typography tightening: no legacy tracking, poster-scale numerals, or wrapped action buttons. */
.momo-observability-mode :is(
.obs-kicker,

View File

@@ -1761,6 +1761,101 @@
}
}
/* v3.8 dot-matrix integration: surfaces carry the product texture without legacy gradients. */
.momo-observability-mode {
--obs-matrix-dot: radial-gradient(color-mix(in srgb, var(--obs-accent) 14%, transparent) 0.85px, transparent 0.95px);
--obs-matrix-dot-soft: radial-gradient(color-mix(in srgb, var(--obs-muted) 12%, transparent) 0.75px, transparent 0.9px);
--obs-matrix-size: 13px 13px;
}
.momo-observability-mode :is(
.obs-hero,
.agent-hero,
.biz-command,
.runtime-hero,
.calls-hero,
.gov-hero,
.gate-hero,
.rag-hero,
.qa-hero,
.quality-hero,
.ppt-hero,
.obs-panel,
.agent-panel,
.biz-panel,
.runtime-panel,
.calls-panel,
.gov-panel,
.gate-panel,
.rag-panel,
.qa-panel,
.quality-panel,
.ppt-panel,
.obs-signal,
.agent-signal,
.biz-signal,
.runtime-signal,
.calls-signal,
.gov-signal,
.gate-signal,
.rag-signal,
.qa-signal,
.quality-signal,
.ppt-signal,
.biz-filter-card,
.biz-alert-strip,
.biz-strategy-card,
.biz-mini-metric,
.biz-decision-card,
.episode-card,
.similar-box,
.fix-card,
.root-card,
.rec-card,
.agent-card,
.caller-card,
.host-lane,
.strategy-card,
.obs-route-card
) {
background-color: var(--momo-bg-surface, #faf6ec) !important;
background-image: var(--obs-matrix-dot) !important;
background-position: 0 0 !important;
background-size: var(--obs-matrix-size) !important;
}
.momo-observability-mode :is(
.obs-panel,
.agent-panel,
.biz-panel,
.runtime-panel,
.calls-panel,
.gov-panel,
.gate-panel,
.rag-panel,
.qa-panel,
.quality-panel,
.ppt-panel,
.biz-filter-card,
.biz-strategy-card,
.episode-card,
.host-lane,
.caller-card,
.agent-card,
.rec-card,
.fix-card,
.root-card,
.strategy-card
) {
background-image: var(--obs-matrix-dot-soft) !important;
}
.momo-observability-mode :is(.obs-pill, [class$="-pill"], .biz-badge, .badge) {
background-image: var(--obs-matrix-dot-soft) !important;
background-position: 0 0 !important;
background-size: 10px 10px !important;
}
/* v3.7 typography tightening: no legacy tracking, poster-scale numerals, or wrapped action buttons. */
.momo-observability-mode :is(
.obs-kicker,