release: bump version to 0.27.4
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,13 @@ This project follows the repository versioning rule:
|
|||||||
- `improvement` -> `+0.0.1`(bugfix + 小功能混合)
|
- `improvement` -> `+0.0.1`(bugfix + 小功能混合)
|
||||||
- `bugfix` -> `+0.0.1`
|
- `bugfix` -> `+0.0.1`
|
||||||
|
|
||||||
|
## [0.27.4] — 2026-04-14
|
||||||
|
|
||||||
|
### 🔧 Improvements
|
||||||
|
- info-card 改为懒加载动态挂载:页面初始 DOM 不再含隐藏的 `#info-panel` 节点,仅首次点击交互元素时创建
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [0.27.3] — 2026-04-14
|
## [0.27.3] — 2026-04-14
|
||||||
|
|
||||||
### 🔧 Improvements
|
### 🔧 Improvements
|
||||||
|
|||||||
@@ -16,12 +16,13 @@
|
|||||||
## Current Version
|
## Current Version
|
||||||
|
|
||||||
- `main` 当前主线历史推导到:`0.16.5`
|
- `main` 当前主线历史推导到:`0.16.5`
|
||||||
- `dev` 当前开发分支历史推导到:`0.27.3`
|
- `dev` 当前开发分支历史推导到:`0.27.4`
|
||||||
|
|
||||||
## Timeline
|
## Timeline
|
||||||
|
|
||||||
| Version | Type | Branch | Commit | Summary |
|
| Version | Type | Branch | Commit | Summary |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `0.27.4` | improvement | `dev` | — | info-card 懒加载动态挂载,页面初始不再有隐藏节点 |
|
||||||
| `0.27.3` | improvement | `dev` | — | TV panel 折叠方向稳定、视频跳动修复、图例折叠按钮修复、搜索图标调整 |
|
| `0.27.3` | improvement | `dev` | — | TV panel 折叠方向稳定、视频跳动修复、图例折叠按钮修复、搜索图标调整 |
|
||||||
| `0.27.2` | improvement | `dev` | — | 修复 brand copy 宽度问题,提取 --brand-copy-width CSS 变量 |
|
| `0.27.2` | improvement | `dev` | — | 修复 brand copy 宽度问题,提取 --brand-copy-width CSS 变量 |
|
||||||
| `0.27.1` | improvement | `dev` | — | HUD 面板拖拽 L 形边界约束、brand 组件整体缩放、图层面板宽度优化、搜索叉叉修复 |
|
| `0.27.1` | improvement | `dev` | — | HUD 面板拖拽 L 形边界约束、brand 组件整体缩放、图层面板宽度优化、搜索叉叉修复 |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "planet-frontend",
|
"name": "planet-frontend",
|
||||||
"version": "0.27.3",
|
"version": "0.27.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "bun@1",
|
"packageManager": "bun@1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -145,20 +145,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Floating detail panel — positioned near click by JS -->
|
<div id="error-message" class="hud-error-message"></div>
|
||||||
<div id="info-panel" class="hud-panel hud-panel-info hud-panel-draggable" aria-live="polite">
|
|
||||||
<div id="info-card" class="info-card">
|
|
||||||
<div class="info-card-header hud-panel-drag-handle">
|
|
||||||
<span class="info-card-icon" id="info-card-icon">🛰️</span>
|
|
||||||
<h3 id="info-card-title">详情</h3>
|
|
||||||
<button class="info-card-close hud-panel-close" type="button" aria-label="关闭详情">
|
|
||||||
<span class="material-symbols-rounded">close</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div id="info-card-content" class="info-card-content"></div>
|
|
||||||
</div>
|
|
||||||
<div id="error-message" class="hud-error-message"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="right-toolbar-group" class="earth-toolbar-group">
|
<div id="right-toolbar-group" class="earth-toolbar-group">
|
||||||
<div id="control-toolbar" class="earth-toolbar">
|
<div id="control-toolbar" class="earth-toolbar">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { showStatusMessage } from './ui.js';
|
import { showStatusMessage } from './ui.js';
|
||||||
|
|
||||||
let currentType = null;
|
let currentType = null;
|
||||||
|
let cardMounted = false;
|
||||||
|
|
||||||
const CARD_CONFIG = {
|
const CARD_CONFIG = {
|
||||||
cable: {
|
cable: {
|
||||||
@@ -105,6 +106,138 @@ function getPanel() {
|
|||||||
return document.getElementById('info-panel');
|
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 = `
|
||||||
|
<div id="info-card" class="info-card">
|
||||||
|
<div class="info-card-header hud-panel-drag-handle">
|
||||||
|
<span class="info-card-icon" id="info-card-icon">🛰️</span>
|
||||||
|
<h3 id="info-card-title">详情</h3>
|
||||||
|
<button class="info-card-close hud-panel-close" type="button" aria-label="关闭详情">
|
||||||
|
<span class="material-symbols-rounded">close</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="info-card-content" class="info-card-content"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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) {
|
function positionPanel(panel, x, y) {
|
||||||
if (!panel) return;
|
if (!panel) return;
|
||||||
const margin = 12;
|
const margin = 12;
|
||||||
@@ -142,71 +275,8 @@ function hidePanel() {
|
|||||||
if (panel) panel.classList.remove('is-visible');
|
if (panel) panel.classList.remove('is-visible');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initInfoCard() {
|
// No-op: event binding now happens lazily in mountCard()
|
||||||
const card = document.getElementById('info-card');
|
export function initInfoCard() {}
|
||||||
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';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setInfoCardNoBorder(noBorder = true) {
|
export function setInfoCardNoBorder(noBorder = true) {
|
||||||
const card = document.getElementById('info-card');
|
const card = document.getElementById('info-card');
|
||||||
@@ -222,6 +292,8 @@ export function showInfoCard(type, data, options = {}) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mountCard();
|
||||||
|
|
||||||
currentType = type;
|
currentType = type;
|
||||||
const card = document.getElementById('info-card');
|
const card = document.getElementById('info-card');
|
||||||
const icon = document.getElementById('info-card-icon');
|
const icon = document.getElementById('info-card-icon');
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "planet"
|
name = "planet"
|
||||||
version = "0.27.3"
|
version = "0.27.4"
|
||||||
description = "智能星球计划 - 态势感知系统"
|
description = "智能星球计划 - 态势感知系统"
|
||||||
requires-python = ">=3.14"
|
requires-python = ">=3.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|||||||
Reference in New Issue
Block a user