From 67f82dc41c35fa1ee9841b32ba7e3d05aeb718e1 Mon Sep 17 00:00:00 2001 From: rayd1o Date: Thu, 23 Apr 2026 07:56:10 +0800 Subject: [PATCH] release: bump version to 0.37.0 --- TODO.md | 8 + VERSION | 2 +- docs/CHANGELOG.md | 19 + .../earth-compute-center-bgp-style-plan.md | 60 +++ docs/version-history.md | 3 +- frontend/package.json | 2 +- frontend/public/earth/css/hud.css | 46 ++ frontend/public/earth/css/info-panel.css | 45 +- frontend/public/earth/css/layer-panel.css | 23 + frontend/public/earth/index.html | 17 + .../public/earth/js/bgp-cruise-adapter.js | 365 ++++++++++++-- frontend/public/earth/js/bgp.js | 40 +- frontend/public/earth/js/cables.js | 28 +- frontend/public/earth/js/callout-connector.js | 470 +++++++++++++++++- frontend/public/earth/js/compute-centers.js | 115 +++-- frontend/public/earth/js/constants.js | 21 +- frontend/public/earth/js/controls.js | 242 ++++++--- frontend/public/earth/js/info-card.js | 84 +++- frontend/public/earth/js/main.js | 17 +- frontend/public/earth/js/utils.js | 32 ++ pyproject.toml | 2 +- uv.lock | 2 +- 22 files changed, 1417 insertions(+), 226 deletions(-) diff --git a/TODO.md b/TODO.md index 92a3a977..82c462a8 100644 --- a/TODO.md +++ b/TODO.md @@ -24,3 +24,11 @@ - [ ] 如果后续明确需要“账号级同步 Earth 偏好”,再单独设计 `Earth user preferences`:优先按用户维度而不是全局系统设置保存,并规划 `localStorage -> backend` 的平滑迁移策略 - [ ] 把 Earth 态势新闻源从 [earth_news.py](/home/ray/dev/linkong/planet/backend/app/services/earth_news.py) 的硬编码列表抽成可配置目录,优先保持当前“实时聚合”链路不变,只先解决新闻源不可配置的问题 - [ ] 为 Earth 态势新闻设计后续采集器化方案:明确新闻数据模型、去重策略、区域映射、过期清理和 Earth/AI 复用方式,再决定何时把新闻从实时抓取升级成正式 collector +- [ ] 为未知位置的算力中心建立分层坐标补全链路:优先 `精确坐标 > 站点/园区命中 > 城市 > 州/省 > 国家内主要算力城市 > 国家质心`,并把每次回退的 `confidence / reason / precision` 明确写进统一 GeoJSON +- [ ] 为算力中心补一份可维护的本地位置注册表,例如 `canonical_name / aliases / operator / country / region / city / lat / lon / confidence / source_note`,避免把地点知识长期硬编码在 `visualization.py` +- [ ] 增强 `epoch_ai_gpu` 和相关算力采集器的源页面解析:即使公开 API 不给坐标,也继续尝试从详情页、HTML、内嵌 JSON、schema.org、OpenGraph、脚本变量和 PDF/新闻稿链接里抽地点线索 +- [ ] 为未知位置算力中心增加外部富化策略评估:可选接入公开知识源或搜索兜底,只抓“站点名/园区名/城市名”级别线索,不直接抓经纬度结论,并把结果作为候选证据而不是真值 +- [ ] 为算力中心建立 `operator / cluster name / facility alias` 归一化层,先解决 `xAI / Colossus / Memphis`、`OpenAI / Stargate`、`CoreWeave`、`Lambda`、`Crusoe` 这类同一对象多种写法导致的地点匹配失败 +- [ ] 为估算位置增加更细的视觉和产品表达:除了问号角标,还要支持 tooltip/详情中的“估算依据”“精度级别”“最后核验时间”,并允许在设置中单独开关“仅看精确位置” +- [ ] 为国家级估算点设计更合理的落点策略:优先落在“该国主要算力/数据中心城市候选集”而不是几何质心,必要时同国多节点做稳定散列分配,避免大量节点堆在荒漠或海上 +- [ ] 为未知位置算力中心建立人工校验工作流:支持导出待核验清单、记录人工确认结果,并把人工确认反哺到位置注册表,逐步减少问号点比例 diff --git a/VERSION b/VERSION index 93d4c1ef..0f1a7dfc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.36.0 +0.37.0 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d6a79592..521a1c03 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -8,6 +8,25 @@ This project follows the repository versioning rule: - `improvement` -> `+0.0.1`(bugfix + 小功能混合) - `bugfix` -> `+0.0.1` +## [0.35.1] — 2026-04-22 +## [0.37.0] — 2026-04-23 + +### ✨ Highlights +- Earth 连线系统正式从巡航里解耦成通用 callout connector:桌面端和移动端统一支持对象级锚点、四边切换与临界区边缘滑动 +- BGP 巡航展示继续收口为稳定的“先定位卡片、再连真实锚点、再展示卡片”链路,移动端 popup 与桌面 info panel 的路线规则统一 + +### 🔧 Improvements +- connector 配置从 `CRUISE_CONFIG` 拆到独立 `CONNECTOR_CONFIG`,默认类名、动画名和实例命名也全部去 cruise 语义 +- 移动端 popup 增加更稳定的 dock/obstacle 处理,拖动卡片时连线起终点会持续按几何关系自适应刷新 +- Earth 多个图层与控制逻辑继续收口,补充算力中心/BGP 风格对齐、layer panel 与相关交互细节调整 + +### 🐛 Fixes +- 修复巡航模式下终点只像“视觉锚点”而不是真实绑定对象的问题,卡片拖动后终点现在会跟随 +- 修复移动端与桌面端多类连线路线异常:压线、反向、临界区折返、起点遮挡事件点等问题 +- 修复对象矩形临界区内连线仍强制中点到中点导致路线像“先钻进 source 内部”再出去的问题 + +--- + ## [0.35.1] — 2026-04-22 ## [0.36.0] — 2026-04-22 diff --git a/docs/plans/earth-compute-center-bgp-style-plan.md b/docs/plans/earth-compute-center-bgp-style-plan.md index 56913e3b..da414c17 100644 --- a/docs/plans/earth-compute-center-bgp-style-plan.md +++ b/docs/plans/earth-compute-center-bgp-style-plan.md @@ -275,6 +275,66 @@ 这样可以避免一开始把范围摊得过大。 +## Unknown Location Strategy + +由于部分算力数据源不会直接提供经纬度,未知位置补全不能只依赖“继续找 API 字段”。 +更稳妥的方式是做成一条分层富化链路,而不是单一猜测规则。 + +推荐按下面优先级推进: + +1. 直接源信息 + +- 源记录显式给出 `latitude / longitude` +- 源记录给出 `city / region / facility / campus / operator` +- 源页面详情、内嵌 JSON、结构化元数据、新闻稿链接里能抽出地点线索 + +2. 名称与机构归一化 + +- 建立 `canonical_name / aliases / operator / facility` 归一化表 +- 把 `cluster name`、`operator`、`campus name` 归一到同一个实体 +- 优先解决同一对象多写法导致的命中失败,而不是先扩大猜测范围 + +3. 本地位置注册表 + +- 用仓库内可维护的 registry 保存高价值对象的位置知识 +- 每条记录至少包含:`canonical_name`、`aliases`、`operator`、`country`、`region`、`city`、`lat`、`lon`、`confidence`、`source_note` +- 转换层优先读取 registry,避免地点知识长期散落在转换代码里 + +4. 分层回退定位 + +- `precise` +- `estimated_site` +- `estimated_city` +- `estimated_region` +- `estimated_national_hub` +- `estimated_country` + +这里建议把“国家内主要算力城市”作为国家质心之前的一层。 +例如没有美国精确位置时,优先考虑已知的主要算力/数据中心城市候选,而不是直接落在几何质心。 + +5. 候选证据富化 + +- 如果源 API 无地点信息,可以允许采集链路读取公开辅助证据 +- 例如机构官网、数据中心介绍页、新闻稿、百科型页面、公开 PDF +- 但只提取“地点线索”,不把外部页面上的经纬度当真值直接写回 + +6. 人工校验闭环 + +- 对高价值且仍然未知的对象输出待核验清单 +- 把人工确认结果回写到位置注册表 +- 后续采集继续优先复用这层人工确认结果 + +### Additional Solution Paths + +除了静态映射表,还可以考虑下面这些办法: + +- 基于国家和运营方建立“主要园区候选集”,用稳定散列把同国未知节点分散到若干可信城市,而不是全部压到一个点 +- 基于数据中心/云厂商公开 region 列表建立 `operator -> city set` 候选映射,用于云 GPU 集群类对象 +- 把“估算依据”结构化,例如 `matched_alias`、`matched_operator`、`matched_city_text`、`fallback_country_hub` +- 给位置补全增加 `last_verified_at`,便于后续按时间重新校验老旧映射 +- 单独维护“不可可靠定位”状态;这类对象仍可在国家级聚合统计中出现,但可以允许用户在地图上过滤掉 +- 后续如果你们愿意投入更多,可把这条链路做成小型 enrichment pipeline,而不是仅在 API 转换时临时判断 + ## Non-Goals 第一阶段不建议做这些内容: diff --git a/docs/version-history.md b/docs/version-history.md index 47477467..d0af32d6 100644 --- a/docs/version-history.md +++ b/docs/version-history.md @@ -16,12 +16,13 @@ ## Current Version - `main` 当前主线历史推导到:`0.16.5` -- `dev` 当前开发分支历史推导到:`0.36.0` +- `dev` 当前开发分支历史推导到:`0.37.0` ## Timeline | Version | Type | Branch | Commit | Summary | | --- | --- | --- | --- | --- | +| `0.37.0` | feature | `dev` | `pending` | Earth 连线系统从巡航语义中完全解耦为通用 callout connector,统一桌面/移动端对象级锚点、临界区锚点滑动与稳定巡航展示链路 | | `0.36.0` | feature | `dev` | `pending` | Earth 新增统一算力中心图层与估算位置展示,继续收口拖拽交互,并补充 AI Provider 指纹与 WSL 局域网访问支撑 | | `0.35.1` | bugfix | `dev` | `pending` | 收口 Earth 桌面 HUD 与移动端抽屉的统一统计绑定机制,修复态势统计在图层切换后的同步遗漏 | | `0.35.0` | feature | `dev` | `pending` | Earth 移动端抽屉系统与悬浮卡片全面上线:手势驱动抽屉、点击物件弹出可拖动详情卡、单指旋转双指缩放地球 | diff --git a/frontend/package.json b/frontend/package.json index 13c0249c..460ae7b2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "planet-frontend", - "version": "0.36.0", + "version": "0.37.0", "private": true, "packageManager": "bun@1", "dependencies": { diff --git a/frontend/public/earth/css/hud.css b/frontend/public/earth/css/hud.css index 9b0d5dc5..ec3a514a 100644 --- a/frontend/public/earth/css/hud.css +++ b/frontend/public/earth/css/hud.css @@ -271,6 +271,7 @@ display: flex; position: fixed; z-index: 260; + overflow: visible; align-items: center; gap: 10px; padding: 10px 12px 10px 12px; @@ -304,6 +305,15 @@ transform: scale(1) translateY(0); } +.layout-mode-mobile .earth-mobile-popup.earth-mobile-popup--anchor-stable { + transform: none; + transition: opacity 0.17s ease; +} + +.layout-mode-mobile .earth-mobile-popup.earth-mobile-popup--anchor-stable.is-visible { + transform: none; +} + .earth-mobile-popup-icon { font-size: 1.25rem; flex-shrink: 0; @@ -342,6 +352,42 @@ flex-shrink: 0; } +.earth-mobile-popup-dock { + --earth-mobile-popup-dock-size: 14px; + --earth-mobile-popup-dock-offset: calc(var(--earth-mobile-popup-dock-size) * -0.5); + position: absolute; + top: 32%; + width: var(--earth-mobile-popup-dock-size); + height: var(--earth-mobile-popup-dock-size); + border-radius: 999px; + background: transparent; + border: 0; + box-shadow: none; + transform: translateY(-50%); + pointer-events: none; +} + +.earth-mobile-popup[data-dock-side="left"] .earth-mobile-popup-dock { + left: var(--earth-mobile-popup-dock-offset); +} + +.earth-mobile-popup[data-dock-side="right"] .earth-mobile-popup-dock { + right: var(--earth-mobile-popup-dock-offset); +} + +.earth-mobile-popup[data-dock-side="top"] .earth-mobile-popup-dock { + top: var(--earth-mobile-popup-dock-offset); + left: 50%; + transform: translateX(-50%); +} + +.earth-mobile-popup[data-dock-side="bottom"] .earth-mobile-popup-dock { + top: auto; + bottom: var(--earth-mobile-popup-dock-offset); + left: 50%; + transform: translateX(-50%); +} + /* ── Mobile drawer ───────────────────────────────────────────── */ .earth-mobile-drawer-overlay, diff --git a/frontend/public/earth/css/info-panel.css b/frontend/public/earth/css/info-panel.css index 55e1d4d0..120611fa 100644 --- a/frontend/public/earth/css/info-panel.css +++ b/frontend/public/earth/css/info-panel.css @@ -153,15 +153,28 @@ transition: opacity 0.22s ease, transform 0.22s ease; + visibility: hidden; + user-select: none; + -webkit-user-select: none; } .hud-panel-info.is-visible { opacity: 1; transform: scale(1) translateY(0); pointer-events: auto; + visibility: visible; } -.info-card-cruise-link { +.hud-panel-info.hud-panel-info--anchor-stable { + transform: none; + transition: opacity 0.22s ease; +} + +.hud-panel-info.hud-panel-info--anchor-stable.is-visible { + transform: none; +} + +.callout-connector { position: absolute; inset: 0; width: 100%; @@ -173,7 +186,7 @@ z-index: 49; } -.info-card-cruise-link polyline { +.callout-connector polyline { fill: none; stroke: rgba(255, 255, 255, 0.98); stroke-width: 2.15; @@ -185,7 +198,7 @@ drop-shadow(0 0 6px rgba(8, 20, 36, 0.1)); } -.info-card-cruise-link circle { +.callout-connector circle { fill: rgba(255, 255, 255, 0.98); stroke: rgba(7, 16, 32, 0.72); stroke-width: 1.0; @@ -197,29 +210,29 @@ transform-origin: center; } -.info-card-cruise-link.is-visible { +.callout-connector.is-visible { opacity: 1; } -.info-card-cruise-link.is-animating polyline { - animation: cruiseConnectorDraw 0.42s cubic-bezier(0.22, 1, 0.36, 1) forwards; +.callout-connector.is-animating polyline { + animation: calloutConnectorDraw 0.42s cubic-bezier(0.22, 1, 0.36, 1) forwards; } -.info-card-cruise-link.is-animating circle { +.callout-connector.is-animating circle { opacity: 0; } -.info-card-cruise-link.is-animating circle:first-of-type { - animation: cruiseConnectorNodeIn 0.14s ease forwards; +.callout-connector.is-animating circle:first-of-type { + animation: calloutConnectorNodeIn 0.14s ease forwards; animation-delay: 0.02s; } -.info-card-cruise-link.is-animating circle:last-of-type { - animation: cruiseConnectorNodeIn 0.16s ease forwards; +.callout-connector.is-animating circle:last-of-type { + animation: calloutConnectorNodeIn 0.16s ease forwards; animation-delay: 0.34s; } -@keyframes cruiseConnectorDraw { +@keyframes calloutConnectorDraw { from { stroke-dashoffset: var(--connector-length, 0px); } @@ -228,7 +241,7 @@ } } -@keyframes cruiseConnectorNodeIn { +@keyframes calloutConnectorNodeIn { from { opacity: 0; transform: scale(0.72); @@ -290,6 +303,8 @@ scrollbar-width: thin; scrollbar-color: rgba(160, 186, 216, 0.34) transparent; pointer-events: auto; + user-select: none; + -webkit-user-select: none; } .info-card-content::-webkit-scrollbar { @@ -327,6 +342,8 @@ cursor: pointer; flex-shrink: 0; transition: color 0.18s ease; + user-select: none; + -webkit-user-select: none; } .info-card-label:hover { @@ -341,6 +358,8 @@ text-align: right; max-width: calc(180px * var(--hud-scale)); word-break: break-word; + user-select: none; + -webkit-user-select: none; } /* Type-specific header accent colors */ diff --git a/frontend/public/earth/css/layer-panel.css b/frontend/public/earth/css/layer-panel.css index b5b58ba0..fb6c6ff9 100644 --- a/frontend/public/earth/css/layer-panel.css +++ b/frontend/public/earth/css/layer-panel.css @@ -160,6 +160,23 @@ .layer-panel-list { display: flex; flex-direction: column; + max-height: calc(5 * (56px * var(--hud-scale))); + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: rgba(160, 186, 216, 0.34) transparent; +} + +.layer-panel-list::-webkit-scrollbar { + width: 4px; +} + +.layer-panel-list::-webkit-scrollbar-track { + background: transparent; +} + +.layer-panel-list::-webkit-scrollbar-thumb { + background: linear-gradient(180deg, rgba(210, 225, 242, 0.2), rgba(126, 154, 185, 0.28)); + border-radius: 999px; } .layer-row { @@ -169,6 +186,7 @@ padding: calc(9px * var(--hud-scale)) calc(10px * var(--hud-scale)); border-bottom: 1px solid var(--hud-line); transition: background 0.14s ease; + min-height: calc(56px * var(--hud-scale)); } .layer-row:last-child { @@ -343,3 +361,8 @@ max-height: min(52vh, 460px); overflow: auto; } + +.layout-mode-mobile .layer-panel-list { + max-height: none; + overflow-y: visible; +} diff --git a/frontend/public/earth/index.html b/frontend/public/earth/index.html index 6edeeafb..04b0a9ab 100644 --- a/frontend/public/earth/index.html +++ b/frontend/public/earth/index.html @@ -16,6 +16,22 @@ }