Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
698863da68 | ||
|
|
4c0b49962c | ||
|
|
ffb3134d93 | ||
|
|
e8742b5d3c |
@@ -8,6 +8,48 @@ This project follows the repository versioning rule:
|
||||
- `improvement` -> `+0.0.1`(bugfix + 小功能混合)
|
||||
- `bugfix` -> `+0.0.1`
|
||||
|
||||
## [0.76.6] — 2026-10-02
|
||||
|
||||
Released: 2026-10-02
|
||||
|
||||
### Highlights
|
||||
- 扩展 breaking ticker 的全域扫光效果,并拉开新闻卡片与上方状态内容的间距。
|
||||
|
||||
### Added / Fixed / Improved
|
||||
- ticker 反射从局部光斑改为覆盖整条横向区域的扫光/闪烁带。
|
||||
- 桌面与移动新闻列表首卡预留顶部呼吸空间,保持内容区层次。
|
||||
|
||||
---
|
||||
|
||||
## [0.76.5] — 2026-10-02
|
||||
|
||||
Released: 2026-10-02
|
||||
|
||||
### Highlights
|
||||
- 增加 breaking 新闻的玻璃反射式警灯脉冲,并修复新闻卡片内容被 flex 压缩的问题。
|
||||
|
||||
### Added / Fixed / Improved
|
||||
- ticker、卡片边框和卡片反射统一使用同一 breaking 等级颜色。
|
||||
- breaking ticker 使用局部红/橙高光脉冲,卡片使用反射带扫过;面板底色保持蓝色。
|
||||
- 新闻卡片固定非收缩布局并预留最小内容高度,避免只剩媒体名和时间。
|
||||
- 同步中英文 Earth 前端开发文档,记录视觉状态与 reduced-motion 行为。
|
||||
|
||||
---
|
||||
|
||||
## [0.76.4] — 2026-10-01
|
||||
|
||||
Released: 2026-10-01
|
||||
|
||||
### Highlights
|
||||
- 修正 breaking 新闻状态显示:ticker 仅在当前滚动到 breaking 新闻时变红,展开面板始终保持蓝色。
|
||||
|
||||
### Added / Fixed / Improved
|
||||
- 选中、焦点、关闭动画与 breaking 数据状态彻底分离,关闭新闻栏不再留下橙色边线。
|
||||
- breaking 新闻按过期时间重新计算,单条新闻卡片继续保留自身 breaking 红色样式。
|
||||
- 增加 ticker breaking 状态单测和浏览器关闭/重开验证。
|
||||
|
||||
---
|
||||
|
||||
## [0.76.3] — 2026-10-01
|
||||
|
||||
Released: 2026-10-01
|
||||
|
||||
@@ -434,9 +434,21 @@ On mobile, compute uses a compact regional overview in the existing drawer. Rows
|
||||
|
||||
Normal borders, coastlines, claim lines, highlights and landing points use `earth-surface.js` to share the HD sphere UV-triangle surface. Borders draw after the texture but before landing points. Highlight lines are always transparent-queue objects with depth testing, preventing through-globe display or overpainting markers. Geometry is computed and cached at construction, not reprojected per frame. See the [render-layer specification](earth-render-layer-order.md).
|
||||
|
||||
### News ticker edge fading
|
||||
### News ticker edge fading and breaking state machine
|
||||
|
||||
A separate pseudo-element paints the ticker background and horizontal rules, fading to full transparency outside the text bounds. The shell has no rectangular backdrop filter, box shadow, or text mask. The region label sizes to its content and the scrolling text follows directly without fading. The ticker stays neutral; breaking levels style the expanded news panel as a whole. Selection, focus and close animation do not mutate the breaking data state, which is recomputed from item expiry timestamps.
|
||||
A separate pseudo-element paints the ticker background and horizontal rules, fading to full transparency outside the text bounds. The shell has no rectangular backdrop filter, box shadow, or text mask. The region label sizes to its content and the scrolling text follows directly without fading.
|
||||
|
||||
The previous breaking rule was global shell promotion: if the payload contained any unexpired breaking story, the highest level (`critical > breaking > watch`) was applied to the ticker or expanded panel. That made a closed panel, or a ticker currently showing a normal story, retain an orange/red state. `breaking_expires_at` eventually cleared the state, while selection, opening and closing did not change the underlying breaking data.
|
||||
|
||||
The current state machine keeps data, ticker visibility, panel lifecycle and selection separate:
|
||||
|
||||
- `getNewsBreakingState()` derives the data state from `items` and `cruise_items`, ignores expired stories, and ranks `critical > breaking > watch`. The expiry timestamp only schedules the next recomputation.
|
||||
- A ticker viewport watcher runs every 120 ms and chooses the item with the greatest viewport overlap, breaking ties by distance to the viewport center. Only when that current item has a non-`none` level does the ticker switch its horizontal rules to `has-breaking-*`; it returns to neutral as soon as the current item is not breaking.
|
||||
- The panel lifecycle is `closed → opening → open → closing → closed`. Opening and closing control morphing, visibility and ticker hiding; they do not mutate the breaking data.
|
||||
- `selectedCruiseStoryId` is independent of breaking severity. Selection, focus, hover and close only change the selected story and reading position; close also clears the selection and filter popover.
|
||||
- The expanded panel always keeps its blue glass surface. Breaking colors remain on the matching story cards, so opening the panel never turns the entire glass panel red; the ticker can still show a red signal when the currently scrolling story is breaking.
|
||||
|
||||
The glass-reflection pulse is implemented: when the current ticker story enters a breaking state, its `::after` emits a low-frequency, limited-amplitude full-width horizontal red/amber sweep and pulse; the matching breaking card gets a single glass reflection sweep through its own `::after`. The panel base remains blue and there is no permanently flashing full border. `prefers-reduced-motion` disables the animation and keeps a low-intensity static cue. This visual sub-state stays attached to the current breaking story and matching card, independent of whether the panel is open.
|
||||
|
||||
Compute cards directly reuse the shared `.hud-panel` background, border, shadow, and glass treatment. Summary, essentials, and detail areas add no separate black surface. Their headers share inset spacing with the layer panel through `.hud-panel__header--inset` / `.layer-panel-header`; primary numbers remain prominent and essential information stays visible.
|
||||
|
||||
|
||||
@@ -567,9 +567,21 @@ Earth 原有 Roboto Condensed 字体已随 `assets/fonts/` 本地分发,许可
|
||||
|
||||
普通国界、海岸、主张线、高亮及登陆点使用 `earth-surface.js` 对齐高清球面的 UV 三角形。国界与高亮在贴图之后、登陆点之前绘制,高亮开启深度测试且始终位于透明渲染队列;不再透过球体显示或盖住登陆点。几何构建时计算并缓存,不增加每帧重投影。具体顺序见[渲染图层规范](earth-render-layer-order.md)。
|
||||
|
||||
### 新闻栏端部渐变
|
||||
### 新闻栏端部渐变与 breaking 状态机
|
||||
|
||||
新闻栏的底色与上下细线由独立伪元素绘制,两端在正文范围之外渐变到完全透明;外框不使用矩形 backdrop-filter、box-shadow 或文字遮罩。地区标签按内容占宽,正文紧接标签,滚动文字不做渐隐。ticker 始终保持中性,breaking 等级只作用于展开的新闻面板整体背景/阴影;选中、焦点和关闭动画不改变 breaking 数据状态,过期后由时间戳重新计算并清除面板状态。
|
||||
新闻栏的底色与上下细线由独立伪元素绘制,两端在正文范围之外渐变到完全透明;外框不使用矩形 backdrop-filter、box-shadow 或文字遮罩。地区标签按内容占宽,正文紧接标签,滚动文字不做渐隐。
|
||||
|
||||
此前的 breaking 规则是:只要本次 payload 中存在未过期的 breaking 新闻,就按 `critical > breaking > watch` 取最高等级,把等级直接映射到新闻壳层。这样会让整条 ticker 或展开面板在当前显示的新闻并非 breaking、甚至面板已经关闭时仍保留橙/红状态,造成“关闭后残留边缘”和“整个面板变色”。`breaking_expires_at` 到期后才清除该状态;选中、打开和关闭不会改变 breaking 数据本身。
|
||||
|
||||
当前状态机将数据态、ticker 显示态、面板生命周期和选中态分开:
|
||||
|
||||
- 数据态由 `getNewsBreakingState()` 从 `items` 与 `cruise_items` 合并计算,只接受未过期项目,并按 `critical > breaking > watch` 排序;到期时间只负责安排下一次重新计算。
|
||||
- ticker 的显示态由滚动视口 watcher 每 120ms 取视口内重叠最多、距离中心最近的当前项目。只有当前项目的 breaking 等级非 `none` 时,ticker 的上下细线才切换为对应的 `has-breaking-*` 颜色;当前项目离开后立即恢复中性。
|
||||
- 面板生命周期 `panelPhase` 只有 `closed → opening → open → closing → closed`。打开/关闭只控制形变、可见性和 ticker 的隐藏,不改变 breaking 数据态。
|
||||
- `selectedCruiseStoryId` 独立于 breaking 等级。选择、聚焦、悬停和关闭只更新选中项与阅读位置;关闭时清空选中项及筛选弹层。
|
||||
- 展开面板始终使用蓝色玻璃底。breaking 只在对应新闻卡片上使用等级颜色,因此点开面板不会把整块玻璃染成红色,之后滚动到 breaking 新闻时仍可让 ticker 显示红色提示。
|
||||
|
||||
breaking 的玻璃反射脉冲已实现:ticker 当前项目进入 breaking 状态时,`::after` 以低频、有限幅度的全宽横向扫光带增强并闪烁;展开面板中只在对应 breaking 新闻卡片上用 `::after` 扫过一条玻璃反射带。面板底色保持蓝色,不使用持续闪烁的整圈边缘;`prefers-reduced-motion` 下停用动画并保留低强度静态提示。该视觉子状态挂在当前 breaking 项目和对应卡片上,不与面板是否打开耦合。
|
||||
|
||||
算力卡片外观直接复用共享 `.hud-panel` 的背景、边框、阴影和玻璃效果,摘要、基础信息及详情区不再叠加单独黑底。标题栏与图层面板共用 `.hud-panel__header--inset` / `.layer-panel-header` 的内边距;主数字继续突出,基础信息保持常显。
|
||||
|
||||
|
||||
@@ -16,12 +16,15 @@
|
||||
## Current Version
|
||||
|
||||
- `main` 当前主线历史推导到:`0.16.5`
|
||||
- `dev` 当前开发分支历史推导到:`0.76.3`
|
||||
- `dev` 当前开发分支历史推导到:`0.76.6`
|
||||
|
||||
## Timeline
|
||||
|
||||
| Version | Type | Branch | Commit | Summary |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `0.76.6` | bugfix | `dev` | `v0.76.6` | breaking ticker 全域扫光闪烁,新闻列表首卡增加顶部呼吸空间 |
|
||||
| `0.76.5` | bugfix | `dev` | `v0.76.5` | breaking 玻璃反射式警灯脉冲、等级颜色统一及新闻卡片内容高度修复 |
|
||||
| `0.76.4` | bugfix | `dev` | `v0.76.4` | 修正 breaking 新闻 ticker 与面板状态边界,当前滚动到 breaking 项时才显示红色 |
|
||||
| `0.76.3` | bugfix | `dev` | `v0.76.3` | 修复新闻栏关闭后残留 breaking 橙色边线,分离 ticker、面板、选择和过期状态 |
|
||||
| `0.76.2` | bugfix | `dev` | `v0.76.2` | 优化光缆密集区卫星点拾取,统一点状对象与线缆候选排序并补齐回归验证 |
|
||||
| `0.76.1` | bugfix | `dev` | `v0.76.1` | 修复选中 BGP 事件静态光晕地表遮挡,统一事件锚点显隐与前景偏移,清理中间态逻辑并补齐验证 |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "planet-frontend",
|
||||
"version": "0.76.3",
|
||||
"version": "0.76.6",
|
||||
"private": true,
|
||||
"packageManager": "bun@1",
|
||||
"dependencies": {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
--news-ticker-width: min(calc(820px * var(--hud-scale)), 58vw);
|
||||
--news-ticker-edge-fade: calc(96px * var(--hud-scale));
|
||||
--news-ticker-rule-color: var(--hud-border);
|
||||
--news-breaking-color: rgba(255, 166, 102, 0.46);
|
||||
position: absolute;
|
||||
top: calc(var(--hud-offset) + calc(2px * var(--hud-scale)));
|
||||
left: 50%;
|
||||
@@ -47,7 +48,50 @@
|
||||
}
|
||||
|
||||
.earth-news-ticker::after {
|
||||
content: none;
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: calc(-8px * var(--hud-scale)) calc(-14px * var(--hud-scale));
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
background: linear-gradient(
|
||||
102deg,
|
||||
transparent 0%,
|
||||
transparent 34%,
|
||||
color-mix(in srgb, var(--news-breaking-color) 18%, transparent) 43%,
|
||||
var(--news-breaking-color) 50%,
|
||||
color-mix(in srgb, var(--news-breaking-color) 18%, transparent) 57%,
|
||||
transparent 66%,
|
||||
transparent 100%
|
||||
) 140% 0 / 220% 100% no-repeat;
|
||||
filter: blur(calc(8px * var(--hud-scale)));
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
|
||||
.earth-news-ticker__viewport {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.earth-news-ticker.has-breaking-watch::after,
|
||||
.earth-news-ticker.has-breaking-breaking::after,
|
||||
.earth-news-ticker.has-breaking-critical::after {
|
||||
animation: earthNewsBreakingGlassPulse 2.4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.earth-news-ticker.has-breaking-watch {
|
||||
--news-breaking-color: rgba(255, 213, 128, 0.42);
|
||||
--news-ticker-rule-color: var(--news-breaking-color);
|
||||
}
|
||||
|
||||
.earth-news-ticker.has-breaking-breaking {
|
||||
--news-breaking-color: rgba(255, 151, 78, 0.5);
|
||||
--news-ticker-rule-color: var(--news-breaking-color);
|
||||
}
|
||||
|
||||
.earth-news-ticker.has-breaking-critical {
|
||||
--news-breaking-color: rgba(255, 95, 95, 0.56);
|
||||
--news-ticker-rule-color: var(--news-breaking-color);
|
||||
}
|
||||
|
||||
.earth-news-ticker__region {
|
||||
@@ -221,6 +265,37 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes earthNewsBreakingGlassPulse {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
background-position: 140% 0;
|
||||
}
|
||||
18%,
|
||||
74% {
|
||||
opacity: 0.76;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.9;
|
||||
background-position: -40% 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes earthNewsBreakingCardReflection {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(-125%);
|
||||
}
|
||||
16%,
|
||||
64% {
|
||||
opacity: 0.58;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateX(125%);
|
||||
}
|
||||
}
|
||||
|
||||
.news-panel-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -442,6 +517,7 @@
|
||||
min-height: 0;
|
||||
max-height: none;
|
||||
overflow-y: auto;
|
||||
padding-top: calc(8px * var(--hud-scale));
|
||||
padding-right: calc(4px * var(--hud-scale));
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(160, 220, 255, 0.36) transparent;
|
||||
@@ -461,6 +537,11 @@
|
||||
}
|
||||
|
||||
.news-story-card {
|
||||
--news-breaking-color: rgba(255, 166, 102, 0.46);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
flex: 0 0 auto;
|
||||
min-height: calc(108px * var(--hud-scale));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(8px * var(--hud-scale));
|
||||
@@ -476,6 +557,15 @@
|
||||
transform 0.18s ease;
|
||||
}
|
||||
|
||||
.news-story-card > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.earth-mobile-news-board-list {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.news-story-card:hover {
|
||||
border-color: rgba(214, 235, 255, 0.16);
|
||||
background:
|
||||
@@ -497,48 +587,45 @@
|
||||
0 0 18px rgba(255, 184, 77, 0.12);
|
||||
}
|
||||
|
||||
.earth-news-hud.has-breaking-watch,
|
||||
.earth-mobile-news-board.has-breaking-watch {
|
||||
background: linear-gradient(180deg, rgba(72, 53, 26, 0.94), rgba(28, 24, 18, 0.96));
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 213, 128, 0.08) inset,
|
||||
0 18px 46px rgba(156, 106, 20, 0.12);
|
||||
}
|
||||
|
||||
.earth-news-hud.has-breaking-breaking,
|
||||
.earth-mobile-news-board.has-breaking-breaking {
|
||||
background: linear-gradient(180deg, rgba(82, 44, 28, 0.94), rgba(32, 22, 18, 0.96));
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 166, 102, 0.14) inset,
|
||||
0 18px 48px rgba(185, 89, 32, 0.16);
|
||||
}
|
||||
|
||||
.earth-news-hud.has-breaking-critical,
|
||||
.earth-mobile-news-board.has-breaking-critical {
|
||||
background: linear-gradient(180deg, rgba(76, 32, 38, 0.94), rgba(30, 18, 24, 0.96));
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 118, 118, 0.18) inset,
|
||||
0 18px 52px rgba(172, 43, 43, 0.18);
|
||||
}
|
||||
|
||||
.news-story-card--breaking-watch {
|
||||
border-color: rgba(255, 213, 128, 0.28);
|
||||
--news-breaking-color: rgba(255, 213, 128, 0.42);
|
||||
border-color: var(--news-breaking-color);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 248, 220, 0.08), rgba(255, 184, 77, 0.05));
|
||||
}
|
||||
|
||||
.news-story-card--breaking-breaking {
|
||||
border-color: rgba(255, 166, 102, 0.36);
|
||||
--news-breaking-color: rgba(255, 151, 78, 0.5);
|
||||
border-color: var(--news-breaking-color);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 205, 150, 0.1), rgba(255, 135, 80, 0.07));
|
||||
}
|
||||
|
||||
.news-story-card--breaking-critical {
|
||||
border-color: rgba(255, 118, 118, 0.42);
|
||||
--news-breaking-color: rgba(255, 95, 95, 0.56);
|
||||
border-color: var(--news-breaking-color);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 155, 155, 0.11), rgba(205, 58, 58, 0.08));
|
||||
}
|
||||
|
||||
.news-story-card--breaking-watch::after,
|
||||
.news-story-card--breaking-breaking::after,
|
||||
.news-story-card--breaking-critical::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(
|
||||
105deg,
|
||||
transparent 32%,
|
||||
var(--news-breaking-color) 49%,
|
||||
transparent 66%
|
||||
);
|
||||
mix-blend-mode: screen;
|
||||
animation: earthNewsBreakingCardReflection 3.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.news-story-card--breaking-global {
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 255, 255, 0.05) inset,
|
||||
@@ -623,6 +710,18 @@
|
||||
padding: calc(16px * var(--hud-scale)) calc(4px * var(--hud-scale));
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.earth-news-ticker.has-breaking-watch::after,
|
||||
.earth-news-ticker.has-breaking-breaking::after,
|
||||
.earth-news-ticker.has-breaking-critical::after,
|
||||
.news-story-card--breaking-watch::after,
|
||||
.news-story-card--breaking-breaking::after,
|
||||
.news-story-card--breaking-critical::after {
|
||||
animation: none;
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1180px) {
|
||||
.earth-news-ticker {
|
||||
--news-ticker-width: min(calc(760px * var(--hud-scale)), 66vw);
|
||||
|
||||
@@ -62,6 +62,7 @@ let morphTimer = null;
|
||||
let morphFrame = null;
|
||||
let panelPhase = "closed";
|
||||
let breakingExpiryTimer = null;
|
||||
let tickerBreakingTimer = null;
|
||||
let newsRealtimeSocket = null;
|
||||
let newsRealtimeReconnectTimer = null;
|
||||
let activeNewsCategoryFilters = null;
|
||||
@@ -413,20 +414,8 @@ function normalizeBreakingScope(scope) {
|
||||
function applyNewsBreakingShellState(nextPayload) {
|
||||
if (breakingExpiryTimer !== null) window.clearTimeout(breakingExpiryTimer);
|
||||
breakingExpiryTimer = null;
|
||||
const { level, expiresAt } = getNewsBreakingState(nextPayload);
|
||||
document
|
||||
.querySelectorAll(".earth-news-hud, .earth-mobile-news-board")
|
||||
.forEach((element) => {
|
||||
if (!(element instanceof HTMLElement)) return;
|
||||
element.classList.remove(
|
||||
"has-breaking-watch",
|
||||
"has-breaking-breaking",
|
||||
"has-breaking-critical",
|
||||
);
|
||||
if (level !== "none") {
|
||||
element.classList.add(`has-breaking-${level}`);
|
||||
}
|
||||
});
|
||||
const { expiresAt } = getNewsBreakingState(nextPayload);
|
||||
scheduleTickerBreakingWatcher();
|
||||
if (expiresAt !== null) {
|
||||
breakingExpiryTimer = window.setTimeout(() => {
|
||||
breakingExpiryTimer = null;
|
||||
@@ -738,6 +727,7 @@ function renderTicker(nextPayload) {
|
||||
}
|
||||
|
||||
if (items.length === 0) {
|
||||
stopTickerBreakingWatcher();
|
||||
tickerTrack.textContent = translateText("正在准备全球态势新闻...");
|
||||
tickerTrack.style.removeProperty("--news-ticker-duration");
|
||||
return;
|
||||
@@ -745,23 +735,69 @@ function renderTicker(nextPayload) {
|
||||
|
||||
const visibleItems = getDisplayableNewsItems(items).slice(0, 6);
|
||||
if (visibleItems.length === 0) {
|
||||
stopTickerBreakingWatcher();
|
||||
tickerTrack.textContent = translateText("当前新闻类型没有可显示新闻...");
|
||||
tickerTrack.style.removeProperty("--news-ticker-duration");
|
||||
return;
|
||||
}
|
||||
const tickerItems = [...visibleItems, ...visibleItems];
|
||||
tickerTrack.innerHTML = tickerItems
|
||||
.map((item) => `
|
||||
<span class="earth-news-ticker__item" data-news-id="${escapeTickerText(item.id || "")}">
|
||||
.map((item) => {
|
||||
const breakingLevel = isBreakingActive(item) ? normalizeBreakingLevel(item.breaking_level) : "none";
|
||||
const breakingClass = breakingLevel === "none" ? "" : ` earth-news-ticker__item--breaking-${breakingLevel}`;
|
||||
return `
|
||||
<span class="earth-news-ticker__item${breakingClass}" data-news-id="${escapeTickerText(item.id || "")}" data-breaking-level="${breakingLevel}">
|
||||
<span class="earth-news-ticker__source">${escapeTickerText(getNewsSourceNameLabel({
|
||||
id: item.source_id,
|
||||
name: item.source || item.feed_name || "NEWS",
|
||||
}))}</span>
|
||||
<span>${escapeTickerText(getNewsDisplaySummary(item))}</span>
|
||||
</span>
|
||||
`)
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
tickerTrack.style.setProperty("--news-ticker-duration", `${Math.max(22, visibleItems.length * 7)}s`);
|
||||
scheduleTickerBreakingWatcher();
|
||||
}
|
||||
|
||||
function stopTickerBreakingWatcher() {
|
||||
if (tickerBreakingTimer !== null) window.clearTimeout(tickerBreakingTimer);
|
||||
tickerBreakingTimer = null;
|
||||
document.getElementById("desktop-news-ticker")?.classList.remove(
|
||||
"has-breaking-watch",
|
||||
"has-breaking-breaking",
|
||||
"has-breaking-critical",
|
||||
);
|
||||
}
|
||||
|
||||
function updateTickerBreakingState() {
|
||||
const ticker = document.getElementById("desktop-news-ticker");
|
||||
const viewport = ticker?.querySelector(".earth-news-ticker__viewport");
|
||||
const items = viewport ? [...viewport.querySelectorAll(".earth-news-ticker__item")] : [];
|
||||
if (!(ticker instanceof HTMLElement) || !(viewport instanceof HTMLElement) || items.length === 0) {
|
||||
stopTickerBreakingWatcher();
|
||||
return;
|
||||
}
|
||||
const viewportRect = viewport.getBoundingClientRect();
|
||||
const viewportCenter = viewportRect.left + viewportRect.width / 2;
|
||||
let best = null;
|
||||
for (const item of items) {
|
||||
const rect = item.getBoundingClientRect();
|
||||
const overlap = Math.max(0, Math.min(rect.right, viewportRect.right) - Math.max(rect.left, viewportRect.left));
|
||||
if (overlap <= 0) continue;
|
||||
const distance = Math.abs((rect.left + rect.right) / 2 - viewportCenter);
|
||||
if (!best || overlap > best.overlap || (overlap === best.overlap && distance < best.distance)) {
|
||||
best = { overlap, distance, level: normalizeBreakingLevel(item.dataset.breakingLevel) };
|
||||
}
|
||||
}
|
||||
ticker.classList.remove("has-breaking-watch", "has-breaking-breaking", "has-breaking-critical");
|
||||
if (best?.level && best.level !== "none") ticker.classList.add(`has-breaking-${best.level}`);
|
||||
tickerBreakingTimer = window.setTimeout(updateTickerBreakingState, 120);
|
||||
}
|
||||
|
||||
function scheduleTickerBreakingWatcher() {
|
||||
stopTickerBreakingWatcher();
|
||||
tickerBreakingTimer = window.setTimeout(updateTickerBreakingState, 0);
|
||||
}
|
||||
|
||||
function renderEmptyState(message) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "planet"
|
||||
version = "0.76.3"
|
||||
version = "0.76.6"
|
||||
description = "智能星球计划 - 态势感知系统"
|
||||
requires-python = ">=3.14"
|
||||
dependencies = [
|
||||
|
||||
Reference in New Issue
Block a user