补齐平台订阅、外部登录、CRM 同步与用户端页面;Nginx 转发用户端接口,登录页使用 Google 与 Apple 标志。 Co-authored-by: Cursor <cursoragent@cursor.com>
790 行
17 KiB
Plaintext
790 行
17 KiB
Plaintext
<template>
|
|
<view class="radar-page" :class="pageRtlClass" :style="pageTheme">
|
|
<view class="radar-top">
|
|
<view class="radar-bar">
|
|
<view class="radar-heading">
|
|
<text class="radar-title">{{ pageTitle }}</text>
|
|
<text class="radar-desc">{{ pageDesc }}</text>
|
|
</view>
|
|
<view class="radar-refresh" @click="handleRefresh">
|
|
<text class="radar-refresh-text">{{ t('refresh') }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="radar-live">
|
|
<view class="radar-live-dot"></view>
|
|
<text class="radar-live-text">{{ liveStatusText }}</text>
|
|
</view>
|
|
|
|
<view v-if="showSummary" class="stat-row">
|
|
<view class="stat-cell">
|
|
<text class="stat-value">{{ stats.totalVisits }}</text>
|
|
<text class="stat-label">{{ t('totalVisits') }}</text>
|
|
</view>
|
|
<view class="stat-cell">
|
|
<text class="stat-value" :class="{ 'stat-value-hot': stats.highIntentCount > 0 }">{{ stats.highIntentCount }}</text>
|
|
<text class="stat-label">{{ t('highIntent') }}</text>
|
|
</view>
|
|
<view class="stat-cell">
|
|
<text class="stat-value">{{ stats.avgStayText }}</text>
|
|
<text class="stat-label">{{ t('avgStay') }}</text>
|
|
</view>
|
|
<view class="stat-cell">
|
|
<text class="stat-value" :class="{ 'stat-value-live': stats.onlineCount > 0 }">{{ stats.onlineCount }}</text>
|
|
<text class="stat-label">{{ t('onlineNow') }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<text v-if="showSummary" class="stat-place">{{ stats.topLocation }}</text>
|
|
</view>
|
|
|
|
<scroll-view class="radar-scroll" :scroll-y="true">
|
|
<view class="radar-body">
|
|
<view v-if="showSkeleton" class="radar-skeleton">
|
|
<view class="skeleton-card"></view>
|
|
<view class="skeleton-card"></view>
|
|
<view class="skeleton-card"></view>
|
|
</view>
|
|
|
|
<view v-else-if="state.errorMessage !== ''" class="radar-feedback">
|
|
<text class="feedback-title">{{ t('error') }}</text>
|
|
<text class="feedback-desc">{{ state.errorMessage }}</text>
|
|
<view class="feedback-retry" @click="handleRefresh">
|
|
<text class="feedback-retry-text">{{ t('refresh') }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-else-if="state.empty" class="radar-feedback">
|
|
<text class="feedback-title">{{ t('empty') }}</text>
|
|
<text class="feedback-desc">{{ t('emptyHint') }}</text>
|
|
</view>
|
|
|
|
<view v-else>
|
|
<view v-if="showOnlineStrip" class="online-strip">
|
|
<text class="online-strip-label">{{ onlineStripLabel }}</text>
|
|
<list-view class="online-list" :style="onlineListStyle">
|
|
<list-item v-for="item in onlineVisitors" :key="item.id">
|
|
<view class="mini-avatar">
|
|
<image class="mini-avatar-img" :src="avatarSrc(item)" mode="aspectFill" />
|
|
<view class="mini-dot"></view>
|
|
</view>
|
|
</list-item>
|
|
</list-view>
|
|
</view>
|
|
<list-view class="radar-list" :style="listStyle">
|
|
<list-item v-for="item in rows" :key="item.id">
|
|
<view
|
|
class="radar-row"
|
|
:class="{
|
|
'radar-row-hot': isHighIntent(item.stayDurationMs, item.scrollDepth),
|
|
'radar-row-amber': isMediumIntent(item.stayDurationMs, item.scrollDepth),
|
|
}"
|
|
>
|
|
<view class="radar-row-main">
|
|
<view class="avatar-wrap">
|
|
<image class="avatar-img" :src="avatarSrc(item)" mode="aspectFill" />
|
|
<view v-if="showOnline(item)" class="avatar-online"></view>
|
|
</view>
|
|
<view class="radar-row-body">
|
|
<view class="radar-row-top">
|
|
<text class="radar-headline">{{ formatName(item) }}</text>
|
|
<text class="radar-time">{{ formatTime(item) }}</text>
|
|
</view>
|
|
<text class="radar-sub">{{ formatAction(item) }} · {{ formatLocation(item.country, item.city) }}</text>
|
|
<text v-if="hasExtra(item)" class="radar-sub">{{ extraLine(item) }}</text>
|
|
<view class="radar-row-meta">
|
|
<text class="radar-meta">{{ formatMeta(item) }}</text>
|
|
<view :class="'intent-' + resolveIntentLevel(item)">
|
|
<text class="intent-text">{{ resolveIntentBadge(item) }}</text>
|
|
</view>
|
|
</view>
|
|
<text v-if="hasBlocks(item)" class="radar-blocks">{{ formatBlocks(item) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</list-item>
|
|
</list-view>
|
|
</view>
|
|
<view class="radar-spacer"></view>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
import { getCurrentLocale, getOmniText, isRtl } from '../../common/locale.uts';
|
|
import { utsLightColors, utsRadius, utsSpacing, utsTypography } from '../../common/tokens.uts';
|
|
import { RadarTrack } from '../../common/types.uts';
|
|
import {
|
|
applyRadarOptions,
|
|
computeRadarStats,
|
|
consolidateRadarTracks,
|
|
createRadarState,
|
|
formatDepth,
|
|
formatLocation,
|
|
formatMonitorTime,
|
|
formatNetworkLine,
|
|
formatStay,
|
|
formatViewedLine,
|
|
formatVisitorAction,
|
|
formatVisitorName,
|
|
getIntentBadgeText,
|
|
getIntentLevel,
|
|
isHighIntent,
|
|
isVisitorOnline,
|
|
refreshTracks,
|
|
resolveRadarText,
|
|
RadarCopyKey,
|
|
RadarPageOptions,
|
|
RadarStats,
|
|
} from './useRadar.uts';
|
|
|
|
const pageTheme: Record<string, string> = {
|
|
'--oc-primary': '#0B5CFF',
|
|
'--oc-primary-soft': utsLightColors.primarySoft,
|
|
'--oc-bg': '#F7F8FA',
|
|
'--oc-surface': utsLightColors.surface,
|
|
'--oc-border': '#E3E7ED',
|
|
'--oc-text': '#101828',
|
|
'--oc-text-2': '#667085',
|
|
'--oc-success': utsLightColors.success,
|
|
'--oc-warning': utsLightColors.warning,
|
|
'--oc-error': '#F04438',
|
|
'--oc-radius-btn': utsRadius.button,
|
|
'--oc-radius-card': utsRadius.card,
|
|
'--oc-radius-modal': utsRadius.modal,
|
|
'--oc-shadow': utsTypography.shadow,
|
|
'--oc-space-8': utsSpacing.space8,
|
|
'--oc-space-12': utsSpacing.space12,
|
|
'--oc-space-16': utsSpacing.space16,
|
|
'--oc-space-24': utsSpacing.space24,
|
|
};
|
|
|
|
const state = reactive(createRadarState());
|
|
const booted = ref(false);
|
|
|
|
const pageTitle = computed((): string => {
|
|
return getOmniText(getCurrentLocale(), 'radar.online_visitor');
|
|
});
|
|
|
|
const pageDesc = computed((): string => {
|
|
return getOmniText(getCurrentLocale(), 'radar.visitor_trace');
|
|
});
|
|
|
|
const pageRtlClass = computed((): string => {
|
|
return isRtl(getCurrentLocale()) ? 'radar-rtl' : '';
|
|
});
|
|
|
|
const rows = computed((): RadarTrack[] => {
|
|
return consolidateRadarTracks(state.tracks);
|
|
});
|
|
|
|
const onlineVisitors = computed((): RadarTrack[] => {
|
|
const list: RadarTrack[] = [];
|
|
for (let i = 0; i < rows.value.length; i++) {
|
|
const item = rows.value[i];
|
|
if (isVisitorOnline(item)) {
|
|
list.push(item);
|
|
}
|
|
}
|
|
return list;
|
|
});
|
|
|
|
const stats = computed((): RadarStats => {
|
|
return computeRadarStats(rows.value, state.total);
|
|
});
|
|
|
|
const liveStatusText = computed((): string => {
|
|
return resolveRadarText('liveRadar');
|
|
});
|
|
|
|
const showSummary = computed((): boolean => {
|
|
return booted.value && state.errorMessage === '' && !state.empty;
|
|
});
|
|
|
|
const showSkeleton = computed((): boolean => {
|
|
if (state.errorMessage !== '') {
|
|
return false;
|
|
}
|
|
if (!booted.value) {
|
|
return true;
|
|
}
|
|
return state.loading && state.tracks.length === 0;
|
|
});
|
|
|
|
const showOnlineStrip = computed((): boolean => {
|
|
return onlineVisitors.value.length > 0;
|
|
});
|
|
|
|
const onlineStripLabel = computed((): string => {
|
|
return t('onlineNow');
|
|
});
|
|
|
|
const onlineListStyle = computed((): string => {
|
|
return 'height:48px';
|
|
});
|
|
|
|
const listStyle = computed((): string => {
|
|
const count = rows.value.length > 0 ? rows.value.length : 1;
|
|
return 'height:' + String(count * 112) + 'px';
|
|
});
|
|
|
|
function t(key: RadarCopyKey): string {
|
|
return resolveRadarText(key);
|
|
}
|
|
|
|
function formatName(item: RadarTrack): string {
|
|
return formatVisitorName(item);
|
|
}
|
|
|
|
function formatAction(item: RadarTrack): string {
|
|
return formatVisitorAction(item);
|
|
}
|
|
|
|
function formatTime(item: RadarTrack): string {
|
|
return formatMonitorTime(item);
|
|
}
|
|
|
|
const GUEST_AVATAR = '/static/icons/avatar-guest.svg';
|
|
|
|
function avatarSrc(item: RadarTrack): string {
|
|
const url = item.avatarUrl != null ? String(item.avatarUrl).trim() : '';
|
|
return url !== '' ? url : GUEST_AVATAR;
|
|
}
|
|
|
|
function extraLine(item: RadarTrack): string {
|
|
const parts: string[] = [];
|
|
const card = cardLine(item);
|
|
const company = companyLine(item);
|
|
const network = networkLine(item);
|
|
if (card !== '') {
|
|
parts.push(card);
|
|
}
|
|
if (company !== '') {
|
|
parts.push(company);
|
|
}
|
|
if (network !== '') {
|
|
parts.push(network);
|
|
}
|
|
return parts.join(' · ');
|
|
}
|
|
|
|
function hasExtra(item: RadarTrack): boolean {
|
|
return extraLine(item) !== '';
|
|
}
|
|
|
|
function showOnline(item: RadarTrack): boolean {
|
|
return isVisitorOnline(item);
|
|
}
|
|
|
|
function hasCompany(item: RadarTrack): boolean {
|
|
return item.visitorCompany != null && String(item.visitorCompany).trim() !== '';
|
|
}
|
|
|
|
function companyLine(item: RadarTrack): string {
|
|
return item.visitorCompany != null ? String(item.visitorCompany).trim() : '';
|
|
}
|
|
|
|
function hasCardTitle(item: RadarTrack): boolean {
|
|
return item.cardTitle != null && String(item.cardTitle).trim() !== '';
|
|
}
|
|
|
|
function cardLine(item: RadarTrack): string {
|
|
return item.cardTitle != null ? String(item.cardTitle).trim() : '';
|
|
}
|
|
|
|
function hasNetwork(item: RadarTrack): boolean {
|
|
return formatNetworkLine(item.ip) !== '';
|
|
}
|
|
|
|
function networkLine(item: RadarTrack): string {
|
|
return formatNetworkLine(item.ip);
|
|
}
|
|
|
|
function formatMeta(item: RadarTrack): string {
|
|
return t('stay') + ' ' + formatStay(item.stayDurationMs) + ' · ' + t('depth') + ' ' + formatDepth(item.scrollDepth);
|
|
}
|
|
|
|
function resolveIntentLevel(item: RadarTrack): string {
|
|
return getIntentLevel(item.stayDurationMs, item.scrollDepth);
|
|
}
|
|
|
|
function isMediumIntent(stayDurationMs: number, scrollDepth: number): boolean {
|
|
return getIntentLevel(stayDurationMs, scrollDepth) === 'medium';
|
|
}
|
|
|
|
function resolveIntentBadge(item: RadarTrack): string {
|
|
const level = resolveIntentLevel(item);
|
|
return getIntentBadgeText(level);
|
|
}
|
|
|
|
function hasBlocks(item: RadarTrack): boolean {
|
|
return formatViewedLine(item) !== '';
|
|
}
|
|
|
|
function formatBlocks(item: RadarTrack): string {
|
|
return formatViewedLine(item);
|
|
}
|
|
|
|
async function handleRefresh(): Promise<void> {
|
|
const wasLoading = state.loading;
|
|
await refreshTracks(state);
|
|
if (wasLoading) {
|
|
return;
|
|
}
|
|
booted.value = true;
|
|
}
|
|
|
|
onLoad((options: RadarPageOptions) => {
|
|
applyRadarOptions(state, options);
|
|
handleRefresh();
|
|
});
|
|
|
|
onShow(() => {
|
|
handleRefresh();
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.radar-page {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 100vw;
|
|
max-width: 480px;
|
|
height: 100vh;
|
|
max-height: 100vh;
|
|
margin-inline: auto;
|
|
background-color: var(--oc-bg, #F7F8FA);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.radar-rtl {
|
|
direction: rtl;
|
|
}
|
|
|
|
.radar-top {
|
|
flex-shrink: 0;
|
|
padding-block-start: calc(8px + env(safe-area-inset-top, 0px));
|
|
padding-inline: 16px;
|
|
padding-block-end: 8px;
|
|
background-color: #F7F8FA;
|
|
}
|
|
|
|
.radar-bar {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
min-height: 44px;
|
|
}
|
|
|
|
.radar-heading {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
margin-inline-end: 8px;
|
|
}
|
|
|
|
.radar-title {
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
line-height: 1.25;
|
|
color: #101828;
|
|
}
|
|
|
|
.radar-desc {
|
|
margin-top: 2px;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
line-height: 1.4;
|
|
color: #667085;
|
|
}
|
|
|
|
.radar-refresh {
|
|
min-width: 44px;
|
|
min-height: 44px;
|
|
padding-inline: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.radar-refresh:active {
|
|
opacity: 0.72;
|
|
}
|
|
|
|
.radar-refresh-text {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #0B5CFF;
|
|
}
|
|
|
|
.radar-live {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
align-self: flex-start;
|
|
margin-block: 4px 12px;
|
|
padding: 4px 10px;
|
|
background-color: #ECFDF3;
|
|
border-radius: 9999px;
|
|
}
|
|
|
|
.radar-live-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
margin-inline-end: 6px;
|
|
border-radius: 4px;
|
|
background-color: #12B76A;
|
|
}
|
|
|
|
.radar-live-text {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
line-height: 1.4;
|
|
color: #027A48;
|
|
}
|
|
|
|
.stat-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
background-color: #FFFFFF;
|
|
border: 1px solid #E3E7ED;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.stat-cell {
|
|
flex: 1;
|
|
min-width: 0;
|
|
min-height: 52px;
|
|
padding: 8px 4px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.stat-cell + .stat-cell {
|
|
border-inline-start: 1px solid #E3E7ED;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
line-height: 1.2;
|
|
color: #101828;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.stat-value-hot {
|
|
color: #F04438;
|
|
}
|
|
|
|
.stat-value-live {
|
|
color: #027A48;
|
|
}
|
|
|
|
.stat-label {
|
|
margin-top: 2px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
line-height: 1.4;
|
|
color: #667085;
|
|
text-align: center;
|
|
}
|
|
|
|
.stat-place {
|
|
margin-block: 8px 4px;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
line-height: 1.4;
|
|
color: #667085;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.radar-scroll {
|
|
flex: 1;
|
|
width: 100%;
|
|
height: 0;
|
|
min-height: 0;
|
|
}
|
|
|
|
.radar-body {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
padding-inline: 16px;
|
|
}
|
|
|
|
.radar-skeleton {
|
|
padding-block-start: 4px;
|
|
}
|
|
|
|
.skeleton-card {
|
|
height: 96px;
|
|
margin-block-end: 8px;
|
|
background-color: #E3E7ED;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.radar-feedback {
|
|
margin-block-start: 8px;
|
|
padding: 28px 16px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
background-color: #FFFFFF;
|
|
border: 1px solid #E3E7ED;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.feedback-title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
line-height: 1.25;
|
|
color: #101828;
|
|
text-align: center;
|
|
}
|
|
|
|
.feedback-desc {
|
|
margin-top: 6px;
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
color: #667085;
|
|
text-align: center;
|
|
}
|
|
|
|
.feedback-retry {
|
|
min-height: 44px;
|
|
margin-top: 16px;
|
|
padding-inline: 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-color: #EBF5FF;
|
|
border-radius: 9999px;
|
|
}
|
|
|
|
.feedback-retry:active {
|
|
opacity: 0.72;
|
|
}
|
|
|
|
.feedback-retry-text {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #0B5CFF;
|
|
}
|
|
|
|
.online-strip {
|
|
margin-block-end: 8px;
|
|
padding: 10px 12px 8px;
|
|
background-color: #FFFFFF;
|
|
border: 1px solid #E3E7ED;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.online-strip-label {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
line-height: 1.4;
|
|
color: #027A48;
|
|
}
|
|
|
|
.online-list {
|
|
width: 100%;
|
|
height: 48px;
|
|
margin-top: 6px;
|
|
display: flex;
|
|
flex-direction: row;
|
|
}
|
|
|
|
.mini-avatar {
|
|
position: relative;
|
|
width: 32px;
|
|
height: 32px;
|
|
margin-inline-end: 8px;
|
|
border-radius: 16px;
|
|
background-color: #EBF5FF;
|
|
overflow: visible;
|
|
}
|
|
|
|
.mini-avatar-img {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 16px;
|
|
background-color: #E8EEF4;
|
|
}
|
|
|
|
.mini-dot {
|
|
position: absolute;
|
|
right: 0;
|
|
bottom: 0;
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 4px;
|
|
background-color: #12B76A;
|
|
border: 1px solid #FFFFFF;
|
|
}
|
|
|
|
.radar-list {
|
|
width: 100%;
|
|
}
|
|
|
|
.radar-row {
|
|
min-height: 0;
|
|
margin-block-end: 6px;
|
|
padding: 8px 10px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background-color: #FFFFFF;
|
|
border: 1px solid #E3E7ED;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.radar-row-main {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.avatar-wrap {
|
|
position: relative;
|
|
width: 32px;
|
|
height: 32px;
|
|
margin-inline-end: 8px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.avatar-img {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 16px;
|
|
background-color: #E8EEF4;
|
|
}
|
|
|
|
.avatar-online {
|
|
position: absolute;
|
|
right: 0;
|
|
bottom: 0;
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 4px;
|
|
background-color: #12B76A;
|
|
border: 1px solid #FFFFFF;
|
|
}
|
|
|
|
.radar-row-body {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.radar-row-hot {
|
|
border-color: #E3E7ED;
|
|
background-color: #FFFFFF;
|
|
}
|
|
|
|
.radar-row-amber {
|
|
border-color: #E3E7ED;
|
|
background-color: #FFFFFF;
|
|
}
|
|
|
|
.radar-row-top {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.radar-headline {
|
|
flex: 1;
|
|
min-width: 0;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
line-height: 1.3;
|
|
color: #101828;
|
|
}
|
|
|
|
.radar-time {
|
|
flex-shrink: 0;
|
|
margin-inline-start: 8px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
line-height: 1.3;
|
|
color: #667085;
|
|
}
|
|
|
|
.radar-sub {
|
|
margin-top: 2px;
|
|
font-size: 12px;
|
|
line-height: 1.35;
|
|
color: #667085;
|
|
}
|
|
|
|
.radar-row-meta {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.radar-meta {
|
|
flex: 1;
|
|
min-width: 0;
|
|
margin-inline-end: 8px;
|
|
font-size: 12px;
|
|
line-height: 1.35;
|
|
color: #667085;
|
|
}
|
|
|
|
.intent-high {
|
|
background-color: transparent;
|
|
padding: 0;
|
|
}
|
|
|
|
.intent-medium,
|
|
.intent-normal {
|
|
background-color: transparent;
|
|
padding: 0;
|
|
}
|
|
|
|
.intent-high .intent-text {
|
|
color: var(--oc-error, #F04438);
|
|
}
|
|
|
|
.intent-medium .intent-text {
|
|
color: #B54708;
|
|
}
|
|
|
|
.intent-normal .intent-text {
|
|
color: #667085;
|
|
}
|
|
|
|
.intent-text {
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.radar-blocks {
|
|
margin-top: 2px;
|
|
font-size: 12px;
|
|
line-height: 1.35;
|
|
color: #667085;
|
|
}
|
|
|
|
.radar-spacer {
|
|
height: calc(16px + var(--window-bottom, 0px) + env(safe-area-inset-bottom, 0px));
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.radar-refresh,
|
|
.feedback-retry {
|
|
transition: none;
|
|
}
|
|
}
|
|
</style>
|