release: bump version to 0.38.0

This commit is contained in:
linkong
2026-04-23 17:57:35 +08:00
parent 195a8bf71c
commit d5f3784ffb
39 changed files with 4958 additions and 146 deletions

View File

@@ -1,7 +1,13 @@
// controls.js - Zoom, rotate and toggle controls
import * as THREE from "three";
import { CONFIG, EARTH_CONFIG, ROTATION_MODE } from "./constants.js";
import {
CONFIG,
CRUISE_MODULES,
DEFAULT_CRUISE_MODULES,
EARTH_CONFIG,
ROTATION_MODE,
} from "./constants.js";
import { setEarthStatValue, updateZoomDisplay, showStatusMessage } from "./ui.js";
import {
toggleTerrain,
@@ -109,6 +115,7 @@ let activeMobileDrawerId = null;
let mobileDrawerOpen = false;
let mobileDrawerCard = "layers";
let mobileDrawerHintTimer = null;
const ALLOWED_CRUISE_MODULES = new Set(Object.values(CRUISE_MODULES));
function detectLayoutMode() {
const width = window.innerWidth;
@@ -633,6 +640,7 @@ function getCurrentPanelVisibilitySnapshot() {
function getCurrentSharedSettingsSnapshot() {
return {
rotationMode,
cruiseModules: getCruiseModules(),
layerVisibility: Object.fromEntries(
getPersistedLayers().map((layer) => [layer.id, Boolean(layer.getVisible?.())]),
),
@@ -667,6 +675,7 @@ function cloneEarthSettings(settings) {
version: 2,
shared: {
rotationMode: settings.shared.rotationMode,
cruiseModules: [...(settings.shared.cruiseModules || DEFAULT_CRUISE_MODULES)],
terrainOpacity: settings.shared.terrainOpacity,
dayNightEnabled: settings.shared.dayNightEnabled,
defaultEarthZoom: settings.shared.defaultEarthZoom,
@@ -738,6 +747,14 @@ function normalizeEarthSettings(rawSettings, defaults) {
sharedSettings?.rotationMode === ROTATION_MODE.CRUISE
? ROTATION_MODE.CRUISE
: defaults.shared.rotationMode;
const requestedCruiseModules = Array.isArray(sharedSettings?.cruiseModules)
? sharedSettings.cruiseModules
: defaults.shared.cruiseModules;
const nextCruiseModules = Array.from(
new Set(
requestedCruiseModules.filter((moduleId) => ALLOWED_CRUISE_MODULES.has(moduleId)),
),
);
const nextTerrainOpacity = Number.parseFloat(sharedSettings?.terrainOpacity);
const nextDayNightEnabled = typeof sharedSettings?.dayNightEnabled === "boolean"
? sharedSettings.dayNightEnabled
@@ -750,6 +767,9 @@ function normalizeEarthSettings(rawSettings, defaults) {
version: 2,
shared: {
rotationMode: nextRotationMode,
cruiseModules: nextCruiseModules.length > 0
? nextCruiseModules
: [...DEFAULT_CRUISE_MODULES],
layerVisibility: normalizedLayerVisibility,
terrainOpacity: Number.isFinite(nextTerrainOpacity)
? nextTerrainOpacity
@@ -832,6 +852,79 @@ function persistEarthSettings() {
}
}
function dispatchCruiseModulesChange() {
window.dispatchEvent(
new CustomEvent("earth:cruise-modules-change", {
detail: {
modules: getCruiseModules(),
},
}),
);
}
function normalizeCruiseModules(nextModules) {
const sourceModules = Array.isArray(nextModules) ? nextModules : DEFAULT_CRUISE_MODULES;
const normalizedModules = Array.from(
new Set(sourceModules.filter((moduleId) => ALLOWED_CRUISE_MODULES.has(moduleId))),
);
return normalizedModules.length > 0
? normalizedModules
: [...DEFAULT_CRUISE_MODULES];
}
function syncCruiseModuleControls() {
const enabledModules = new Set(getCruiseModules());
document.querySelectorAll("[data-cruise-module-toggle]").forEach((button) => {
if (!(button instanceof HTMLButtonElement)) return;
const moduleId = button.dataset.cruiseModuleToggle || "";
const active = enabledModules.has(moduleId);
button.classList.toggle("is-active", active);
button.setAttribute("aria-pressed", active ? "true" : "false");
});
}
export function getCruiseModules() {
const configuredModules = earthSettingsState?.shared?.cruiseModules;
return normalizeCruiseModules(configuredModules);
}
export function isCruiseModuleEnabled(moduleId) {
return getCruiseModules().includes(moduleId);
}
export function setCruiseModules(nextModules, { persist = true, suppressStatus = false } = {}) {
const normalizedModules = normalizeCruiseModules(nextModules);
const previousModules = getCruiseModules();
const changed =
normalizedModules.length !== previousModules.length ||
normalizedModules.some((moduleId, index) => previousModules[index] !== moduleId);
if (!changed) {
syncCruiseModuleControls();
return normalizedModules;
}
earthSettingsState = cloneEarthSettings(
earthSettingsState || cloneEarthSettings(captureEarthSettingsDefaults()),
);
earthSettingsState.shared.cruiseModules = [...normalizedModules];
syncCruiseModuleControls();
dispatchCruiseModulesChange();
if (persist) {
persistEarthSettings();
}
if (!suppressStatus) {
const labels = normalizedModules.map((moduleId) =>
moduleId === CRUISE_MODULES.NEWS ? "新闻" : "BGP",
);
showStatusMessage(`巡航模块已切换为:${labels.join(" + ")}`, "info");
}
return normalizedModules;
}
function syncDefaultEarthZoomUi(nextZoom) {
const sliders = document.querySelectorAll("#default-earth-size-slider, [data-default-earth-size-slider]");
const values = document.querySelectorAll("#default-earth-size-value, [data-default-earth-size-value]");
@@ -894,6 +987,7 @@ async function applyEarthSettings(settings) {
});
setRotationMode(settings.shared.rotationMode, { persist: false, suppressStatus: true });
setCruiseModules(settings.shared.cruiseModules, { persist: false, suppressStatus: true });
if (typeof settings.shared.dayNightEnabled === "boolean") {
applyDayNightEnabled(settings.shared.dayNightEnabled, { persist: false });
@@ -1702,6 +1796,7 @@ function setupSettingsControls() {
const terrainOpacityValues = document.querySelectorAll("#terrain-opacity-value, [data-terrain-opacity-value]");
const defaultEarthSizeSliders = document.querySelectorAll("#default-earth-size-slider, [data-default-earth-size-slider]");
const rotationModeButtons = document.querySelectorAll("[data-rotation-mode]");
const cruiseModuleButtons = document.querySelectorAll("[data-cruise-module-toggle]");
const syncTerrainOpacityUi = (nextOpacity) => {
const safeOpacity = Math.round(nextOpacity * 100);
terrainOpacitySliders.forEach((slider) => {
@@ -1756,6 +1851,24 @@ function setupSettingsControls() {
});
});
cruiseModuleButtons.forEach((button) => {
bindListener(button, "click", (event) => {
const target = event.currentTarget;
if (!(target instanceof HTMLButtonElement)) return;
const moduleId = target.dataset.cruiseModuleToggle;
if (!moduleId) return;
const currentModules = new Set(getCruiseModules());
if (currentModules.has(moduleId)) {
currentModules.delete(moduleId);
} else {
currentModules.add(moduleId);
}
setCruiseModules(Array.from(currentModules));
});
});
document.querySelectorAll("#toggle-daynight, [data-daynight-toggle]").forEach((dayNightToggle) => {
if (!(dayNightToggle instanceof HTMLInputElement)) return;
bindListener(dayNightToggle, "change", () => {
@@ -1772,6 +1885,7 @@ function setupSettingsControls() {
applyEarthSettings(loadEarthSettings());
syncAllHudPanelToggles();
syncRotationModeButtons();
syncCruiseModuleControls();
syncDayNightToggle(dayNightEnabled);
}