release: bump version to 0.29.1

This commit is contained in:
rayd1o
2026-04-20 22:12:19 +08:00
parent fe45a99cbd
commit e6d0332fba
15 changed files with 360 additions and 180 deletions

View File

@@ -1 +1 @@
0.29.0
0.29.1

View File

@@ -8,6 +8,23 @@ This project follows the repository versioning rule:
- `improvement` -> `+0.0.1`bugfix + 小功能混合)
- `bugfix` -> `+0.0.1`
## [0.29.1] — 2026-04-20
### ✨ Highlights
- Earth 加载状态条改成单一队列式通知面板,加载阶段不再因为步骤文案变化而回缩,也不会被其他通知打断
- 调整 brand panel 的呈现方式与昼夜/选中态可读性,让品牌区更自然、交互高亮在白天和黑夜里都更稳定
### 🔧 Improvements
- 移除旧的地球加载浮层结构,统一由 HUD 状态消息承载三点脉冲加载过程
- brand panel 改为无边框品牌层,仅保留轻微氛围光,不再因为非常规尺寸显得像第五块功能面板
- 温和收敛地球昼夜材质与主背光强度,保留昼夜辨识度的同时提升白天地表纹理和夜面交互可见性
### 🐛 Fixes
- 修复加载地球时通知条在步骤切换中反复缩短、其他状态消息抢占加载流程的问题
- 修复海缆、登陆点和 BGP 选中高亮在黑夜中过暗、在高光中过亮导致难以辨识的问题
---
## [0.29.0] — 2026-04-20
### ✨ Highlights

View File

@@ -16,12 +16,13 @@
## Current Version
- `main` 当前主线历史推导到:`0.16.5`
- `dev` 当前开发分支历史推导到:`0.29.0`
- `dev` 当前开发分支历史推导到:`0.29.1`
## Timeline
| Version | Type | Branch | Commit | Summary |
| --- | --- | --- | --- | --- |
| `0.29.1` | bugfix | `dev` | `pending` | Earth 加载通知条改为队列式单面板显示brand panel 去框并收敛昼夜与选中态可读性 |
| `0.29.0` | feature | `dev` | `pending` | Earth 新增天球背景与太阳/月亮位置层,强化昼夜分隔并收口卫星图例与图层面板交互 |
| `0.28.2` | bugfix | `dev` | `pending` | 修正媒体情报 tab 尺寸记忆与切换锚点逻辑,并清理 docs 根目录遗留旧路径文档 |
| `0.28.1` | bugfix | `dev` | `pending` | 收口 Earth 媒体情报面板命名与 tab 文案,整理 docs 分组并归档已完成/废弃计划文档 |

View File

@@ -1,6 +1,6 @@
{
"name": "planet-frontend",
"version": "0.29.0",
"version": "0.29.1",
"private": true,
"packageManager": "bun@1",
"dependencies": {

View File

@@ -83,59 +83,17 @@ body.earth-page {
pointer-events: none;
}
.earth-loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 240;
min-width: min(calc(320px * var(--hud-scale)), 78vw);
padding: calc(26px * var(--hud-scale));
border-radius: calc(18px * var(--hud-scale));
border: 1px solid rgba(77, 184, 255, 0.34);
background:
radial-gradient(circle at 50% 18%, rgba(255, 255, 255, 0.12), transparent 35%),
linear-gradient(180deg, rgba(13, 24, 46, 0.95), rgba(7, 14, 28, 0.94));
box-shadow:
0 0 30px rgba(77, 184, 255, 0.22),
0 16px 40px rgba(0, 0, 0, 0.28);
text-align: center;
color: #4db8ff;
}
.earth-loading-text {
color: #4db8ff;
}
.earth-loading-title {
font-size: calc(1.15rem * var(--hud-scale));
font-weight: 600;
}
.earth-loading-subtitle {
margin-top: calc(10px * var(--hud-scale));
color: #9ab7d4;
font-size: calc(0.84rem * var(--hud-scale));
line-height: 1.45;
}
.earth-loading-spinner {
width: calc(40px * var(--hud-scale));
height: calc(40px * var(--hud-scale));
margin: 0 auto calc(15px * var(--hud-scale));
border: 4px solid rgba(77, 184, 255, 0.28);
border-top: 4px solid #4db8ff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
@keyframes earthLoadingPulse {
0%,
80%,
100% {
opacity: 0.28;
transform: scale(0.78);
}
100% {
transform: rotate(360deg);
40% {
opacity: 1;
transform: scale(1);
}
}

View File

@@ -243,21 +243,32 @@
.earth-status-message {
position: absolute;
top: 20px;
top: calc(20px * var(--hud-scale));
left: 50%;
transform: translate(-50%, -18px);
background:
linear-gradient(180deg, rgba(18, 31, 52, 0.92), rgba(8, 18, 32, 0.9));
border-radius: 14px;
padding: 11px 15px;
z-index: 210;
box-shadow: var(--hud-shadow-soft);
border: 1px solid var(--hud-border);
font-size: 0.9rem;
display: none;
backdrop-filter: blur(10px);
text-align: center;
min-width: 180px;
align-items: center;
gap: calc(10px * var(--hud-scale));
background:
linear-gradient(90deg, rgba(145, 186, 255, 0.07) 0%, transparent 44%),
linear-gradient(180deg, rgba(22, 36, 58, 0.95), rgba(8, 18, 32, 0.93));
border-radius: 999px;
border: 1px solid var(--hud-border);
border-left-color: rgba(145, 186, 255, 0.32);
padding: calc(8px * var(--hud-scale)) calc(20px * var(--hud-scale)) calc(8px * var(--hud-scale)) calc(16px * var(--hud-scale));
z-index: 210;
box-shadow:
var(--hud-shadow-soft),
0 0 18px rgba(145, 186, 255, 0.06);
font-size: calc(0.84rem * var(--hud-scale));
font-weight: 500;
letter-spacing: 0.01em;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
text-align: left;
min-width: min(calc(160px * var(--hud-scale)), 58vw);
max-width: min(calc(440px * var(--hud-scale)), 74vw);
color: var(--hud-text);
opacity: 0;
transition:
transform 0.28s ease,
@@ -269,19 +280,110 @@
opacity: 1;
}
/* ── Indicator: single dot (transient) or three dots (loading) ── */
.earth-status-indicator {
display: inline-flex;
align-items: center;
gap: calc(5px * var(--hud-scale));
flex: 0 0 auto;
}
.earth-status-dot {
width: calc(7px * var(--hud-scale));
height: calc(7px * var(--hud-scale));
border-radius: 50%;
background: rgba(145, 186, 255, 0.95);
box-shadow:
0 0 8px rgba(145, 186, 255, 0.7),
0 0 20px rgba(145, 186, 255, 0.28);
animation: statusDotPulse 2.4s ease-in-out infinite;
}
.earth-status-text {
flex: 1 1 auto;
min-width: 0;
}
/* Loading: three-dot sequential pulse */
.earth-status-message.loading .earth-status-dot {
animation: earthLoadingPulse 1.2s ease-in-out infinite;
}
.earth-status-message.loading .earth-status-dot:nth-child(2) {
animation-delay: 0.16s;
}
.earth-status-message.loading .earth-status-dot:nth-child(3) {
animation-delay: 0.32s;
}
/* ── Status color variants ───────────────────────────────────── */
.earth-status-message.success {
color: #d8f7df;
border-left: 3px solid #66d18f;
color: #dff8e7;
background:
linear-gradient(90deg, rgba(102, 209, 143, 0.08) 0%, transparent 44%),
linear-gradient(180deg, rgba(22, 36, 58, 0.95), rgba(8, 18, 32, 0.93));
border-left-color: rgba(102, 209, 143, 0.38);
box-shadow:
var(--hud-shadow-soft),
0 0 18px rgba(102, 209, 143, 0.06);
}
.earth-status-message.success .earth-status-dot {
background: #66d18f;
box-shadow:
0 0 8px rgba(102, 209, 143, 0.8),
0 0 20px rgba(102, 209, 143, 0.3);
}
.earth-status-message.warning {
color: #fff2c3;
border-left: 3px solid #e4c464;
background:
linear-gradient(90deg, rgba(228, 196, 100, 0.08) 0%, transparent 44%),
linear-gradient(180deg, rgba(22, 36, 58, 0.95), rgba(8, 18, 32, 0.93));
border-left-color: rgba(228, 196, 100, 0.38);
box-shadow:
var(--hud-shadow-soft),
0 0 18px rgba(228, 196, 100, 0.06);
}
.earth-status-message.warning .earth-status-dot {
background: #e4c464;
box-shadow:
0 0 8px rgba(228, 196, 100, 0.8),
0 0 20px rgba(228, 196, 100, 0.3);
}
.earth-status-message.error {
color: #ffd4d7;
border-left: 3px solid #ff7b86;
background:
linear-gradient(90deg, rgba(255, 123, 134, 0.08) 0%, transparent 44%),
linear-gradient(180deg, rgba(22, 36, 58, 0.95), rgba(8, 18, 32, 0.93));
border-left-color: rgba(255, 123, 134, 0.38);
box-shadow:
var(--hud-shadow-soft),
0 0 18px rgba(255, 123, 134, 0.06);
}
.earth-status-message.error .earth-status-dot {
background: #ff7b86;
box-shadow:
0 0 8px rgba(255, 123, 134, 0.8),
0 0 20px rgba(255, 123, 134, 0.34);
}
@keyframes statusDotPulse {
0%, 100% {
opacity: 0.82;
transform: scale(0.9);
}
50% {
opacity: 1;
transform: scale(1);
}
}
.earth-tooltip {

View File

@@ -26,13 +26,23 @@
.hud-panel-brand {
--brand-scale: 0.88;
--brand-copy-width: 160px;
border-radius: 0;
padding: calc(18px * var(--hud-scale)) calc(20px * var(--hud-scale));
padding: calc(10px * var(--hud-scale)) calc(4px * var(--hud-scale)) calc(12px * var(--hud-scale)) 0;
display: flex;
align-items: center;
justify-content: center;
justify-content: flex-start;
/* Reserve full panel height before brand images load */
min-height: calc(66px * var(--hud-scale));
background: transparent;
border: 0;
box-shadow: none;
backdrop-filter: none;
-webkit-backdrop-filter: none;
isolation: isolate;
}
.hud-panel-brand::before,
.hud-panel-brand::after {
display: none;
}
.hud-panel-brand .earth-brand {
@@ -40,6 +50,20 @@
align-items: center;
gap: calc(10px * var(--hud-scale) * var(--brand-scale));
min-width: 0;
position: relative;
}
.hud-panel-brand .earth-brand::after {
content: "";
position: absolute;
inset: calc(4px * var(--hud-scale)) calc(-10px * var(--hud-scale)) calc(6px * var(--hud-scale)) calc(-10px * var(--hud-scale));
background:
radial-gradient(circle at 18% 50%, rgba(145, 186, 255, 0.14), transparent 32%),
linear-gradient(90deg, rgba(145, 186, 255, 0.05), transparent 58%);
opacity: 0.75;
pointer-events: none;
filter: blur(14px);
z-index: -1;
}
.hud-panel-brand .earth-brand__logo {

View File

@@ -397,12 +397,7 @@
<div class="tv-panel-edge" data-edge="bl"></div>
</div>
<div id="loading" class="earth-loading">
<div id="loading-spinner" class="earth-loading-spinner"></div>
<div id="loading-title" class="earth-loading-title earth-loading-text">正在初始化全球态势数据...</div>
<div id="loading-subtitle" class="earth-loading-subtitle">同步卫星、海底光缆、登陆点与BGP态势数据</div>
</div>
<div id="status-message" class="earth-status-message"></div>
<div id="status-message" class="earth-status-message" aria-live="polite" aria-atomic="true"></div>
<div id="tooltip" class="earth-tooltip"></div>
<div id="settings-modal" class="earth-settings-modal" aria-hidden="true">
<div id="settings-backdrop" class="earth-settings-backdrop"></div>

View File

@@ -1386,21 +1386,17 @@ export function updateBGPVisualState(lockedObjectType, lockedObject, camera) {
if (isLocked) {
scale *= 1.1 + 0.14 * pulse;
opacity = BGP_CONFIG.opacity.collectorHover;
haloOpacity = 0.022;
pulseOpacity = 0.012;
coverageOpacity = 0.02;
markerColor = blendHexColors(
BGP_CONFIG.collectorIcon.lockedNeutralColor,
marker.userData.baseColor || BGP_CONFIG.collectorColor,
BGP_CONFIG.collectorIcon.lockedBlend,
);
opacity = 0.96;
haloOpacity = 0.05;
pulseOpacity = 0.024;
coverageOpacity = 0.036;
markerColor = 0xcff2ff;
} else if (isHovered) {
scale *= 1.08;
opacity = BGP_CONFIG.opacity.collectorHover;
haloOpacity = 0.016;
pulseOpacity = 0.008;
coverageOpacity = 0.014;
opacity = 0.88;
haloOpacity = 0.03;
pulseOpacity = 0.014;
coverageOpacity = 0.02;
markerColor = blendHexColors(
BGP_CONFIG.collectorIcon.hoverNeutralColor,
marker.userData.baseColor || BGP_CONFIG.collectorColor,
@@ -1482,12 +1478,13 @@ export function updateBGPVisualState(lockedObjectType, lockedObject, camera) {
if (isLocked || isLinkedCollectorLocked) {
scale *= 1 + BGP_CONFIG.pulse.lockedAmplitude * pulse;
opacity =
BGP_CONFIG.opacity.lockedMin +
(BGP_CONFIG.opacity.lockedMax - BGP_CONFIG.opacity.lockedMin) * pulse;
0.9 +
0.1 * pulse;
markerColor = 0xfff1a8;
ringBaseOpacity *= 1.2;
} else if (isHovered) {
scale *= BGP_CONFIG.marker.hoverScale;
opacity = BGP_CONFIG.opacity.hover;
opacity = 0.9;
ringBaseOpacity *= 1.05;
} else if (isOtherLocked) {
scale *= BGP_CONFIG.marker.dimmedScale;

View File

@@ -558,14 +558,18 @@ export function applyLandingPointVisualState(lockedCableName, dimAll = false, ca
lp.userData.cableNames.some((name) => relatedNames.includes(name));
if (isRelated) {
lp.material.color.setHex(CABLE_CONFIG.landingPoint.color);
lp.material.emissive.setHex(CABLE_CONFIG.landingPoint.emissive);
lp.material.color.setHex(0xffd27a);
lp.material.emissive.setHex(0x7a4a00);
lp.material.emissiveIntensity =
CABLE_CONFIG.landingPointVisual.related.emissiveIntensityBase +
pulse * CABLE_CONFIG.landingPointVisual.related.emissiveIntensityPulse;
0.2 +
pulse * (CABLE_CONFIG.landingPointVisual.related.emissiveIntensityPulse + 0.2);
lp.material.opacity =
CABLE_CONFIG.landingPointVisual.related.opacityBase +
pulse * CABLE_CONFIG.landingPointVisual.related.opacityPulse;
Math.max(
0.92,
CABLE_CONFIG.landingPointVisual.related.opacityBase +
pulse * CABLE_CONFIG.landingPointVisual.related.opacityPulse,
);
const distanceScale = getLandingPointDistanceScale(lp, camera);
const baseScale = lp.userData?.baseScale || CABLE_CONFIG.landingPoint.baseScale;
lp.scale.setScalar(

View File

@@ -68,9 +68,9 @@ export const CELESTIAL_CONFIG = {
brightStarMaxScale: 6.8,
brightStarOpacity: 0.88,
sunLightDistance: 460,
sunLightIntensity: 1.28,
sunLightIntensity: 1.02,
sunLightColor: 0xfff4df,
backLightIntensity: 0.18,
backLightIntensity: 0.3,
backLightColor: 0x2b4c78,
};
@@ -300,12 +300,12 @@ export const EARTH_MATERIAL_CONFIG = {
dayNight: {
enabled: true,
sunDirection: { x: 1, y: 0.2, z: 0.4 },
nightFloor: 0.18,
dayBoost: 1.08,
twilightWidth: 0.18,
twilightIntensity: 0.22,
nightFloor: 0.32,
dayBoost: 0.94,
twilightWidth: 0.24,
twilightIntensity: 0.14,
twilightColor: 0x4ea0ff,
nightTintColor: 0x0b1830,
nightTintIntensity: 0.1,
nightTintIntensity: 0.05,
},
};

View File

@@ -700,14 +700,17 @@ function applyCableVisualState() {
switch (state) {
case CABLE_STATE.LOCKED:
cable.material.opacity =
CABLE_CONFIG.lockedOpacityMin +
pulse *
(CABLE_CONFIG.lockedOpacityMax - CABLE_CONFIG.lockedOpacityMin);
cable.material.color.setRGB(1, 1, 1);
Math.max(
0.92,
CABLE_CONFIG.lockedOpacityMin +
pulse *
(CABLE_CONFIG.lockedOpacityMax - CABLE_CONFIG.lockedOpacityMin),
);
cable.material.color.setRGB(0.86, 0.96, 1.0);
break;
case CABLE_STATE.HOVERED:
cable.material.opacity = 1;
cable.material.color.setRGB(1, 1, 1);
cable.material.color.setRGB(0.92, 0.98, 1.0);
break;
case CABLE_STATE.NORMAL:
default:
@@ -1039,7 +1042,7 @@ async function loadData() {
clearCableData(earth);
clearSatelliteData();
setLoadingMessage("正在初始化...", "清除旧数据");
setLoadingMessage("正在初始化...");
setLoading(true);
await yieldFrame();
if (loadToken !== currentLoadToken) { isDataLoading = false; return; }
@@ -1048,7 +1051,7 @@ async function loadData() {
// Step 1 — Landing points
if (cablesEnabled) {
setLoadingMessage("正在加载登陆点...", "同步海底光缆登陆站数据");
setLoadingMessage("正在加载登陆点...");
await yieldFrame(30);
try {
await loadLandingPoints(scene, earth);
@@ -1061,7 +1064,7 @@ async function loadData() {
// Step 2 — Cables
if (cablesEnabled) {
setLoadingMessage("正在加载海缆...", "同步海底光缆网络数据");
setLoadingMessage("正在加载海缆...");
await yieldFrame(30);
try {
const cableCount = await loadGeoJSONFromPath(scene, earth);
@@ -1080,7 +1083,7 @@ async function loadData() {
// Step 3 — Satellites
if (satellitesEnabled) {
setLoadingMessage("正在加载卫星...", "同步在轨卫星轨道数据");
setLoadingMessage("正在加载卫星...");
await yieldFrame(30);
try {
clearSatelliteData();
@@ -1100,7 +1103,7 @@ async function loadData() {
}
// Step 4 — BGP
setLoadingMessage("正在加载BGP态势...", "同步全球路由观测数据");
setLoadingMessage("正在加载BGP态势...");
await yieldFrame(30);
try {
const bgpResult = await loadBGPAnomalies(scene, earth);
@@ -1115,7 +1118,7 @@ async function loadData() {
await yieldFrame();
// Step 5 — Earth texture (loads last so data layers appear on the white sphere first)
setLoadingMessage("正在加载地球纹理...", "加载8K卫星地图");
setLoadingMessage("正在加载地球纹理...");
await yieldFrame(30);
try {
await loadEarthTexture();
@@ -1127,19 +1130,10 @@ async function loadData() {
// Step 6 — Terrain (if enabled)
if (getShowTerrain()) {
setLoadingMessage("正在渲染地形...", "生成地表高程数据");
setLoadingMessage("正在渲染地形...");
await yieldFrame(50);
}
if (errors.length > 0) {
const errorMessage = buildLoadErrorMessage(errors);
showError(errorMessage);
showStatusMessage(errorMessage, "error");
} else {
hideError();
showStatusMessage("数据已加载", "success");
}
updateStatsSummary();
updateCableToggleUi(cablesEnabled);
updateSatelliteToggleUi(satellitesEnabled);
@@ -1149,6 +1143,15 @@ async function loadData() {
refreshLegend();
setLoading(false);
isDataLoading = false;
if (errors.length > 0) {
const errorMessage = buildLoadErrorMessage(errors);
showError(errorMessage);
showStatusMessage(errorMessage, "error");
} else {
hideError();
showStatusMessage("数据已加载", "success");
}
}
const POSITION_UPDATE_FORCE_DELTA = 250;
@@ -1170,7 +1173,7 @@ export async function setCablesEnabled(enabled) {
return 0;
}
setLoadingMessage("正在加载线缆数据...", "重建海缆与登陆点对象");
setLoadingMessage("正在加载线缆数据...");
setLoading(true);
hideError();
@@ -1203,7 +1206,7 @@ export async function setSatellitesEnabled(enabled) {
return 0;
}
setLoadingMessage("正在加载卫星数据...", "重建卫星点位与轨迹缓存");
setLoadingMessage("正在加载卫星数据...");
setLoading(true);
hideError();

View File

@@ -2,8 +2,13 @@
let statusTimeoutId = null;
let statusHideTimeoutId = null;
let statusReplayTimeoutId = null;
const STATUS_BASE_CLASS = "earth-status-message";
const STATUS_DISPLAY_MS = 3000;
const STATUS_FADE_MS = 280;
let statusQueue = [];
let statusBusy = false;
let loadingActive = false;
let loadingLockedWidth = 0;
function getElement(id) {
return document.getElementById(id);
@@ -19,53 +24,97 @@ function clearStatusTimers() {
clearTimeout(statusTimeoutId);
statusTimeoutId = null;
}
if (statusHideTimeoutId) {
clearTimeout(statusHideTimeoutId);
statusHideTimeoutId = null;
}
}
if (statusReplayTimeoutId) {
clearTimeout(statusReplayTimeoutId);
statusReplayTimeoutId = null;
function clearLoadingWidthLock(statusEl) {
loadingLockedWidth = 0;
if (statusEl) {
statusEl.style.minWidth = "";
}
}
// Show status message
export function showStatusMessage(message, type = "info") {
function updateLoadingWidthLock(statusEl) {
if (!statusEl || !loadingActive) return;
const nextWidth = Math.ceil(statusEl.getBoundingClientRect().width || statusEl.scrollWidth || 0);
if (nextWidth <= 0) return;
loadingLockedWidth = Math.max(loadingLockedWidth, nextWidth);
statusEl.style.minWidth = `${loadingLockedWidth}px`;
}
function buildStatusContent(statusEl, message, type) {
statusEl.innerHTML = "";
const indicator = document.createElement("span");
indicator.className = "earth-status-indicator";
indicator.setAttribute("aria-hidden", "true");
const dotCount = type === "loading" ? 3 : 1;
for (let i = 0; i < dotCount; i++) {
const dot = document.createElement("span");
dot.className = "earth-status-dot";
indicator.appendChild(dot);
}
const text = document.createElement("span");
text.className = "earth-status-text";
text.textContent = message;
statusEl.appendChild(indicator);
statusEl.appendChild(text);
}
function hideStatusElement(statusEl, onHidden) {
statusEl.classList.remove("visible");
statusHideTimeoutId = setTimeout(() => {
if (!statusEl.classList.contains("visible")) {
setElementDisplay(statusEl, false);
statusEl.className = STATUS_BASE_CLASS;
statusEl.innerHTML = "";
}
statusHideTimeoutId = null;
if (typeof onHidden === "function") {
onHidden();
}
}, STATUS_FADE_MS);
}
function processStatusQueue() {
if (loadingActive || statusBusy || statusQueue.length === 0) return;
const next = statusQueue.shift();
if (!next) return;
startTransientStatus(next.message, next.type);
}
function startTransientStatus(message, type = "info") {
const statusEl = getElement("status-message");
if (!statusEl) return;
clearStatusTimers();
statusBusy = true;
const startShow = () => {
statusEl.textContent = message;
statusEl.className = `${STATUS_BASE_CLASS} ${type}`;
setElementDisplay(statusEl, true);
statusEl.offsetHeight;
statusEl.classList.add("visible");
buildStatusContent(statusEl, message, type);
statusEl.className = `${STATUS_BASE_CLASS} ${type}`;
setElementDisplay(statusEl, true, "inline-flex");
statusEl.offsetHeight;
statusEl.classList.add("visible");
statusTimeoutId = setTimeout(() => {
statusEl.classList.remove("visible");
statusHideTimeoutId = setTimeout(() => {
setElementDisplay(statusEl, false);
statusEl.textContent = "";
statusHideTimeoutId = null;
}, 280);
statusTimeoutId = null;
}, 3000);
};
statusTimeoutId = setTimeout(() => {
hideStatusElement(statusEl, () => {
statusBusy = false;
processStatusQueue();
});
statusTimeoutId = null;
}, STATUS_DISPLAY_MS);
}
if (statusEl.classList.contains("visible")) {
statusEl.classList.remove("visible");
statusReplayTimeoutId = setTimeout(() => {
startShow();
statusReplayTimeoutId = null;
}, 180);
return;
}
startShow();
// Show status message
export function showStatusMessage(message, type = "info") {
statusQueue.push({ message, type });
processStatusQueue();
}
// Update coordinates display
@@ -120,23 +169,49 @@ export function updateEarthStats(stats) {
textureQualityEl.textContent = stats.textureQuality || "8K 卫星图";
}
// Show/hide loading
// Show/hide loading via status message
export function setLoading(loading) {
const loadingEl = getElement("loading");
if (!loadingEl) return;
setElementDisplay(loadingEl, loading);
const statusEl = getElement("status-message");
if (!statusEl) return;
if (loading) {
clearStatusTimers();
loadingActive = true;
statusBusy = false;
clearLoadingWidthLock(statusEl);
buildStatusContent(statusEl, "正在加载...", "loading");
statusEl.className = `${STATUS_BASE_CLASS} loading`;
setElementDisplay(statusEl, true, "inline-flex");
statusEl.offsetHeight;
statusEl.classList.add("visible");
requestAnimationFrame(() => {
updateLoadingWidthLock(statusEl);
});
} else {
if (!statusEl.classList.contains("loading")) {
loadingActive = false;
clearLoadingWidthLock(statusEl);
processStatusQueue();
return;
}
loadingActive = false;
hideStatusElement(statusEl, () => {
clearLoadingWidthLock(statusEl);
statusBusy = false;
processStatusQueue();
});
}
}
export function setLoadingMessage(title, subtitle = "") {
const titleEl = getElement("loading-title");
const subtitleEl = getElement("loading-subtitle");
if (titleEl) {
titleEl.textContent = title;
}
if (subtitleEl) {
subtitleEl.textContent = subtitle;
export function setLoadingMessage(title) {
const statusEl = getElement("status-message");
if (!statusEl || !statusEl.classList.contains("loading")) return;
const textEl = statusEl.querySelector(".earth-status-text");
if (textEl) {
textEl.textContent = title;
requestAnimationFrame(() => {
updateLoadingWidthLock(statusEl);
});
}
}
@@ -177,12 +252,16 @@ export function hideError() {
export function clearUiState() {
clearStatusTimers();
statusQueue = [];
statusBusy = false;
loadingActive = false;
const statusEl = getElement("status-message");
if (statusEl) {
statusEl.className = STATUS_BASE_CLASS;
setElementDisplay(statusEl, false);
statusEl.textContent = "";
statusEl.innerHTML = "";
clearLoadingWidthLock(statusEl);
}
hideTooltip();

View File

@@ -1,6 +1,6 @@
[project]
name = "planet"
version = "0.29.0"
version = "0.29.1"
description = "智能星球计划 - 态势感知系统"
requires-python = ">=3.14"
dependencies = [

2
uv.lock generated
View File

@@ -475,7 +475,7 @@ wheels = [
[[package]]
name = "planet"
version = "0.29.0"
version = "0.29.1"
source = { virtual = "." }
dependencies = [
{ name = "aiofiles" },