From 40e51d5b203967634a03d896cbd54b236dadc4ef Mon Sep 17 00:00:00 2001 From: linkong Date: Tue, 14 Apr 2026 13:11:39 +0800 Subject: [PATCH] release: bump version to 0.27.4 Co-Authored-By: Claude Sonnet 4.6 --- VERSION | 2 +- docs/CHANGELOG.md | 7 + docs/version-history.md | 3 +- frontend/package.json | 2 +- frontend/public/earth/index.html | 15 +- frontend/public/earth/js/info-card.js | 202 +++++++++++++++++--------- pyproject.toml | 2 +- uv.lock | 2 +- 8 files changed, 151 insertions(+), 84 deletions(-) diff --git a/VERSION b/VERSION index b38e1e76..9f222923 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.27.3 +0.27.4 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 289156b7..09e66bc3 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -8,6 +8,13 @@ This project follows the repository versioning rule: - `improvement` -> `+0.0.1`(bugfix + 小功能混合) - `bugfix` -> `+0.0.1` +## [0.27.4] — 2026-04-14 + +### 🔧 Improvements +- info-card 改为懒加载动态挂载:页面初始 DOM 不再含隐藏的 `#info-panel` 节点,仅首次点击交互元素时创建 + +--- + ## [0.27.3] — 2026-04-14 ### 🔧 Improvements diff --git a/docs/version-history.md b/docs/version-history.md index 11472986..4cf398d0 100644 --- a/docs/version-history.md +++ b/docs/version-history.md @@ -16,12 +16,13 @@ ## Current Version - `main` 当前主线历史推导到:`0.16.5` -- `dev` 当前开发分支历史推导到:`0.27.3` +- `dev` 当前开发分支历史推导到:`0.27.4` ## Timeline | Version | Type | Branch | Commit | Summary | | --- | --- | --- | --- | --- | +| `0.27.4` | improvement | `dev` | — | info-card 懒加载动态挂载,页面初始不再有隐藏节点 | | `0.27.3` | improvement | `dev` | — | TV panel 折叠方向稳定、视频跳动修复、图例折叠按钮修复、搜索图标调整 | | `0.27.2` | improvement | `dev` | — | 修复 brand copy 宽度问题,提取 --brand-copy-width CSS 变量 | | `0.27.1` | improvement | `dev` | — | HUD 面板拖拽 L 形边界约束、brand 组件整体缩放、图层面板宽度优化、搜索叉叉修复 | diff --git a/frontend/package.json b/frontend/package.json index fdda1612..a7cffa27 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "planet-frontend", - "version": "0.27.3", + "version": "0.27.4", "private": true, "packageManager": "bun@1", "dependencies": { diff --git a/frontend/public/earth/index.html b/frontend/public/earth/index.html index 150e4499..06c2baeb 100644 --- a/frontend/public/earth/index.html +++ b/frontend/public/earth/index.html @@ -145,20 +145,7 @@ - -
-
-
- 🛰️ -

详情

- -
-
-
-
-
+
diff --git a/frontend/public/earth/js/info-card.js b/frontend/public/earth/js/info-card.js index 73abbec4..585b2b3e 100644 --- a/frontend/public/earth/js/info-card.js +++ b/frontend/public/earth/js/info-card.js @@ -2,6 +2,7 @@ import { showStatusMessage } from './ui.js'; let currentType = null; +let cardMounted = false; const CARD_CONFIG = { cable: { @@ -105,6 +106,138 @@ function getPanel() { return document.getElementById('info-panel'); } +function setupInfoCardDrag(panel) { + const app = document.getElementById('container'); + if (!app) return; + + const handle = panel.querySelector('.hud-panel-drag-handle'); + if (!handle) return; + + let isDragging = false; + let startPointerX = 0; + let startPointerY = 0; + let startLeft = 0; + let startTop = 0; + + const stopDragging = () => { + isDragging = false; + panel.classList.remove('is-dragging'); + document.body.style.userSelect = ''; + }; + + const onMove = (event) => { + if (!isDragging) return; + const appRect = app.getBoundingClientRect(); + const panelRect = panel.getBoundingClientRect(); + const nextLeft = Math.min( + Math.max(startLeft + (event.clientX - startPointerX), 0), + appRect.width - panelRect.width, + ); + const nextTop = Math.min( + Math.max(startTop + (event.clientY - startPointerY), 0), + appRect.height - panelRect.height, + ); + panel.style.left = `${nextLeft}px`; + panel.style.top = `${nextTop}px`; + }; + + handle.addEventListener('pointerdown', (event) => { + if (event.target.closest('.hud-panel-close, .info-card-close')) return; + isDragging = true; + startPointerX = event.clientX; + startPointerY = event.clientY; + const appRect = app.getBoundingClientRect(); + const panelRect = panel.getBoundingClientRect(); + startLeft = panelRect.left - appRect.left; + startTop = panelRect.top - appRect.top; + panel.style.left = `${startLeft}px`; + panel.style.top = `${startTop}px`; + panel.style.right = 'auto'; + panel.style.bottom = 'auto'; + panel.classList.add('is-dragging'); + document.body.style.userSelect = 'none'; + handle.setPointerCapture?.(event.pointerId); + }); + + handle.addEventListener('pointermove', onMove); + handle.addEventListener('pointerup', stopDragging); + handle.addEventListener('pointercancel', stopDragging); + handle.addEventListener('lostpointercapture', stopDragging); +} + +function mountCard() { + if (cardMounted) return; + + const container = document.getElementById('container'); + if (!container) return; + + const panel = document.createElement('div'); + panel.id = 'info-panel'; + panel.className = 'hud-panel hud-panel-info'; + panel.setAttribute('aria-live', 'polite'); + panel.innerHTML = ` +
+
+ 🛰️ +

详情

+ +
+
+
+ `; + + container.appendChild(panel); + + const card = panel.querySelector('#info-card'); + const content = panel.querySelector('#info-card-content'); + + // Prevent pointer events from reaching the earth canvas + const stopEvent = (event) => { event.stopPropagation(); }; + [ + 'mousemove', 'mousedown', 'mouseup', 'click', 'dblclick', 'wheel', + 'pointerdown', 'pointerup', 'pointermove', + 'touchstart', 'touchmove', 'touchend', + ].forEach((evt) => card.addEventListener(evt, stopEvent, { passive: false })); + + // Close button + const closeBtn = card.querySelector('.info-card-close'); + if (closeBtn) { + closeBtn.addEventListener('click', (event) => { + event.stopPropagation(); + hideInfoCard(); + }); + } + + // Copy value on label click + content.addEventListener('click', async (event) => { + const label = event.target.closest('.info-card-label'); + if (!label) return; + + const property = label.closest('.info-card-property'); + const valueEl = property?.querySelector('.info-card-value'); + const value = valueEl?.textContent?.trim(); + + if (!value || value === '-') { + showStatusMessage('无可复制内容', 'warning'); + return; + } + + try { + await navigator.clipboard.writeText(value); + showStatusMessage(`已复制${label.textContent}:${value}`, 'success'); + } catch (error) { + console.error('Copy failed:', error); + showStatusMessage('复制失败', 'error'); + } + }); + + setupInfoCardDrag(panel); + + cardMounted = true; +} + function positionPanel(panel, x, y) { if (!panel) return; const margin = 12; @@ -142,71 +275,8 @@ function hidePanel() { if (panel) panel.classList.remove('is-visible'); } -export function initInfoCard() { - const card = document.getElementById('info-card'); - const content = document.getElementById('info-card-content'); - if (!card || !content) return; - - if (card.dataset.interactionBound !== 'true') { - const stopEvent = (event) => { - event.stopPropagation(); - }; - - [ - 'mousemove', - 'mousedown', - 'mouseup', - 'click', - 'dblclick', - 'wheel', - 'pointerdown', - 'pointerup', - 'pointermove', - 'touchstart', - 'touchmove', - 'touchend', - ].forEach((eventName) => { - card.addEventListener(eventName, stopEvent, { passive: false }); - }); - - // Close button wires the panel hide - const closeBtn = card.querySelector('.info-card-close'); - if (closeBtn) { - closeBtn.addEventListener('click', (event) => { - event.stopPropagation(); - hideInfoCard(); - }); - } - - card.dataset.interactionBound = 'true'; - } - - if (content.dataset.copyBound === 'true') return; - - content.addEventListener('click', async (event) => { - const label = event.target.closest('.info-card-label'); - if (!label) return; - - const property = label.closest('.info-card-property'); - const valueEl = property?.querySelector('.info-card-value'); - const value = valueEl?.textContent?.trim(); - - if (!value || value === '-') { - showStatusMessage('无可复制内容', 'warning'); - return; - } - - try { - await navigator.clipboard.writeText(value); - showStatusMessage(`已复制${label.textContent}:${value}`, 'success'); - } catch (error) { - console.error('Copy failed:', error); - showStatusMessage('复制失败', 'error'); - } - }); - - content.dataset.copyBound = 'true'; -} +// No-op: event binding now happens lazily in mountCard() +export function initInfoCard() {} export function setInfoCardNoBorder(noBorder = true) { const card = document.getElementById('info-card'); @@ -222,6 +292,8 @@ export function showInfoCard(type, data, options = {}) { return; } + mountCard(); + currentType = type; const card = document.getElementById('info-card'); const icon = document.getElementById('info-card-icon'); diff --git a/pyproject.toml b/pyproject.toml index b298abeb..081b6cc9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "planet" -version = "0.27.3" +version = "0.27.4" description = "智能星球计划 - 态势感知系统" requires-python = ">=3.14" dependencies = [ diff --git a/uv.lock b/uv.lock index 704d6150..9deb45f5 100644 --- a/uv.lock +++ b/uv.lock @@ -475,7 +475,7 @@ wheels = [ [[package]] name = "planet" -version = "0.27.3" +version = "0.27.4" source = { virtual = "." } dependencies = [ { name = "aiofiles" },