diff --git a/VERSION b/VERSION index fb96d142..3c51832d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.27.7 +0.27.8 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a362a008..55a8be11 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,6 +10,20 @@ This project follows the repository versioning rule: ## [0.27.7] — 2026-04-16 +## [0.27.8] — 2026-04-20 + +### 🔧 Improvements +- Earth HUD 共享 `HUDPanel` 默认展开/收缩逻辑继续收口,图例与图层面板统一使用同一套边缘阈值与箭头状态机 +- 保持新闻直播面板现有特例折叠行为不变,避免播放器区域被默认折叠逻辑影响 + +### 🐛 Fixes +- 修复图例与图层面板展开/收缩箭头方向和实际动作不一致的问题 +- 修复拖动到屏幕底边附近时初始箭头、拖动中箭头和点击后动作不同步的问题 + +--- + +## [0.27.7] — 2026-04-16 + ### 🔧 Improvements - 用户管理、数据源配置、电视直播源表格统一接入可折叠操作列,窄宽度下自动收起到下拉菜单,减少操作区挤压 - 电视直播设置改为表格总览 + 弹窗编辑模式,主表内容更紧凑,适合控制台一屏浏览 diff --git a/docs/version-history.md b/docs/version-history.md index 33c2b468..552c7bd1 100644 --- a/docs/version-history.md +++ b/docs/version-history.md @@ -16,12 +16,13 @@ ## Current Version - `main` 当前主线历史推导到:`0.16.5` -- `dev` 当前开发分支历史推导到:`0.27.7` +- `dev` 当前开发分支历史推导到:`0.27.8` ## Timeline | Version | Type | Branch | Commit | Summary | | --- | --- | --- | --- | --- | +| `0.27.8` | bugfix | `dev` | `pending` | 统一 Earth HUD 默认折叠逻辑,修复图例与图层面板箭头和底边阈值行为 | | `0.27.7` | bugfix | `dev` | `pending` | 修复电视直播源编辑持久化问题,清理表格空白占位列并统一可折叠操作列 | | `0.27.6` | improvement | `dev` | `pending` | BGP/用户表格滚动条修复,Playground 响应式按钮与输入框收起优化 | | `0.27.5` | bugfix | `dev` | `pending` | 统一控制台自定义滚动条,修复 alerts/BGP 响应式滚动与采集进度完成态显示 | diff --git a/frontend/package.json b/frontend/package.json index 4628325b..589ca690 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "planet-frontend", - "version": "0.27.7", + "version": "0.27.8", "private": true, "packageManager": "bun@1", "dependencies": { diff --git a/frontend/public/earth/js/controls.js b/frontend/public/earth/js/controls.js index fda5390c..7f771dc7 100644 --- a/frontend/public/earth/js/controls.js +++ b/frontend/public/earth/js/controls.js @@ -18,6 +18,7 @@ import { import { getShowCables } from "./cables.js"; import { toggleBGP, getShowBGP, getBGPCount } from "./bgp.js"; import { ensureTVPanelReady } from "./tv.js"; +import { createHUDPanel } from "./hud-panels.js"; import { ensureNewsPanelReady, setNewsPanelVisible, @@ -631,13 +632,20 @@ function setupLayerPanel() { const emptyState = document.getElementById("layer-panel-empty"); if (!panel) return; + const layerPanel = createHUDPanel({ + panel, + header: ".layer-panel-header", + body: "#layer-panel-body", + collapseBtn, + collapsedClass: "layer-panel--collapsed", + preferredDirection: "down", + expandLabel: "展开图层列表", + collapseLabel: "折叠图层列表", + }); + bindListener(collapseBtn, "click", (e) => { e.stopPropagation(); - const isCollapsed = panel.classList.toggle("layer-panel--collapsed"); - collapseBtn.title = isCollapsed ? "展开" : "折叠"; - collapseBtn.setAttribute("aria-label", isCollapsed ? "展开图层列表" : "折叠图层列表"); - const icon = collapseBtn.querySelector(".material-symbols-rounded"); - if (icon) icon.textContent = isCollapsed ? "expand_more" : "expand_less"; + layerPanel.setCollapsed(!layerPanel.isCollapsed()); }); if (searchInput) { diff --git a/frontend/public/earth/js/hud-panels.js b/frontend/public/earth/js/hud-panels.js index 76da1e36..06d21f69 100644 --- a/frontend/public/earth/js/hud-panels.js +++ b/frontend/public/earth/js/hud-panels.js @@ -1,6 +1,20 @@ const DEFAULT_COLLAPSED_CLASS = "hud-panel--collapsed"; const DEFAULT_HIDDEN_CLASS = "hud-panel-hidden"; -const EXPAND_DIRECTION_BUFFER_PX = 20; + +function getHudScale() { + const rootStyle = getComputedStyle(document.documentElement); + const scale = parseFloat(rootStyle.getPropertyValue("--hud-scale")); + return Number.isFinite(scale) && scale > 0 ? scale : 1; +} + +function getEdgeFlipThresholdPx() { + const rootStyle = getComputedStyle(document.documentElement); + const hudOffset = parseFloat(rootStyle.getPropertyValue("--hud-offset")); + if (Number.isFinite(hudOffset) && hudOffset > 0) { + return hudOffset; + } + return 20 * getHudScale(); +} function clampExpandDirection(direction) { return direction === "up" ? "up" : "down"; @@ -16,41 +30,20 @@ function resolveElement(target, root = document) { } function pickExpandDirection({ - headerRect, - expandedHeight, + panelRect, preferredDirection, - currentDirection, }) { - const spaceAbove = Math.max(0, headerRect.top); - const spaceBelow = Math.max(0, window.innerHeight - headerRect.bottom); + const spaceBelow = Math.max(0, window.innerHeight - panelRect.bottom); + const edgeFlipThresholdPx = getEdgeFlipThresholdPx(); const preferred = clampExpandDirection(preferredDirection); - const fitsAbove = expandedHeight <= spaceAbove; - const fitsBelow = expandedHeight <= spaceBelow; - const bufferedFitsBelow = expandedHeight + EXPAND_DIRECTION_BUFFER_PX <= spaceBelow; - const activeDirection = clampExpandDirection(currentDirection ?? preferred); - // Hysteresis: - // - if we're already in "up", keep it until bottom space drops below h - // - if we're already in "down", keep it until bottom space grows beyond h + 20 - if (activeDirection === "up" && spaceBelow > expandedHeight && fitsAbove) { - return "up"; - } - - if (activeDirection === "down" && spaceBelow < expandedHeight + EXPAND_DIRECTION_BUFFER_PX && fitsBelow) { - return "down"; - } - - // Main contract: - // d = spaceBelow, h = expandedHeight - // - d > h + 20 => up - // - d <= h + 20 => down - // Only fall back when the preferred side cannot actually fit. - if (bufferedFitsBelow && fitsAbove) return "up"; - if (!bufferedFitsBelow && fitsBelow) return "down"; - if (fitsAbove && fitsBelow) return preferred; - if (fitsAbove) return "up"; - if (fitsBelow) return "down"; - return spaceAbove > spaceBelow ? "up" : "down"; + // Pure edge-threshold contract: + // - d < threshold => "up" family + // - d >= threshold => "down" family + // Do not pre-flip early based on expanded height. + if (spaceBelow <= edgeFlipThresholdPx) return "up"; + if (spaceBelow > edgeFlipThresholdPx) return "down"; + return preferred; } function getCollapseButtonState({ collapsed, direction, expandLabel, collapseLabel }) { @@ -125,10 +118,8 @@ export function createHUDPanel({ const syncDirection = () => { const expandedHeight = Math.max(bodyEl.scrollHeight, bodyEl.getBoundingClientRect().height); const nextDirection = pickExpandDirection({ - headerRect: headerEl.getBoundingClientRect(), - expandedHeight, + panelRect: panelEl.getBoundingClientRect(), preferredDirection, - currentDirection, }); currentDirection = nextDirection; @@ -194,15 +185,23 @@ export function createHUDPanel({ }; const handleViewportChange = () => { - if (!panelEl.classList.contains(collapsedClass) && !panelEl.classList.contains(hiddenClass)) { + if (!panelEl.classList.contains(hiddenClass)) { + syncLayout(); + } + }; + + const handlePointerMove = () => { + if (!panelEl.classList.contains(hiddenClass) && panelEl.classList.contains("is-dragging")) { syncLayout(); } }; window.addEventListener("resize", handleViewportChange); document.addEventListener("pointerup", handleViewportChange); + document.addEventListener("pointermove", handlePointerMove); syncLayout(); + requestAnimationFrame(syncLayout); return { panel: panelEl, @@ -215,6 +214,7 @@ export function createHUDPanel({ destroy() { window.removeEventListener("resize", handleViewportChange); document.removeEventListener("pointerup", handleViewportChange); + document.removeEventListener("pointermove", handlePointerMove); }, isCollapsed() { return panelEl.classList.contains(collapsedClass); diff --git a/pyproject.toml b/pyproject.toml index 9d5d02ee..85293cd6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "planet" -version = "0.27.7" +version = "0.27.8" description = "智能星球计划 - 态势感知系统" requires-python = ">=3.14" dependencies = [ diff --git a/uv.lock b/uv.lock index 1268bce0..d09d7cbb 100644 --- a/uv.lock +++ b/uv.lock @@ -475,7 +475,7 @@ wheels = [ [[package]] name = "planet" -version = "0.27.7" +version = "0.27.8" source = { virtual = "." } dependencies = [ { name = "aiofiles" },