Compare commits

..
1 Commits
Author SHA1 Message Date
AxTps d3a510eee6 fix: 引导步骤切换时自动滚动到目标元素
Sync source to CNB / Sync Git ref (push) Skipped
新手引导进行到"更新设置"这一步时,目标按钮位于设置页左侧导航列表的
滚动区域内,可能在视口之外。此前引导只读取目标位置、不会滚动过去,
气泡指向屏幕外,玩家看不到"更新"按钮,容易反复点击版本号。

新增 ensureTargetVisible():步骤切换后若目标不在视口内,就平滑滚动
到视口中央,滚动后刷新目标位置让气泡跟随。
2026-10-17 15:37:42 +08:00
6 changed files with 31 additions and 72 deletions

No files matched your search

@@ -32,31 +32,6 @@ export const ANNOUNCEMENT_CHANGE_TYPES: readonly AnnouncementChangeType[] = [
]
export const launcherAnnouncements: readonly LauncherAnnouncement[] = [
{
id: 'launcher-1.0.2-beta7',
version: '1.0.2-beta7',
publishedAt: '2026-10-17',
title: {
'en-US': 'Starlight Launcher 1.0.2-beta7',
'zh-CN': 'Starlight Launcher 1.0.2-beta7',
},
changes: {
fixed: [
{
'en-US': 'Fixed real-time log backlog that could stall the console under heavy output.',
'zh-CN': '修复实时日志积压导致控制台卡顿的问题。',
},
{
'en-US': 'The update channel now defaults to the build type: installing a Beta build starts on the Beta channel instead of Release.',
'zh-CN': '更新通道默认值改为跟随安装包版本:安装 Beta 版后默认使用 Beta 通道,不再回落为 Release。',
},
{
'en-US': 'On Linux, the default game directory for StarLight instances no longer points at the system install path (e.g. /usr/bin); it falls back to the home directory.',
'zh-CN': 'Linux 上 StarLight 实例的默认游戏目录不再指向系统安装路径(如 /usr/bin),改为回退到用户主目录。',
},
],
},
},
{
id: 'launcher-1.0.2-beta6',
version: '1.0.2-beta6',
@@ -197,6 +197,27 @@ export function useOnboardingTour(
requestAnimationFrame(updateRect)
}
// Bring the current step target into view when the tour jumps to a step
// whose target lives inside a scrollable container (e.g. the settings
// sidebar). Without this the bubble can point at an off-screen element.
function ensureTargetVisible() {
const target = targetElement.value
if (!target) return
const rect = target.getBoundingClientRect()
const outOfView =
rect.top < 0 ||
rect.left < 0 ||
rect.bottom > window.innerHeight ||
rect.right > window.innerWidth
if (!outOfView) return
target.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'smooth' })
requestAnimationFrame(() => {
if (targetElement.value === target) {
targetRect.value = target.getBoundingClientRect()
}
})
}
function goTo(destination: StepDestination) {
if (destination === 'complete') {
events.complete()
@@ -343,6 +364,7 @@ export function useOnboardingTour(
await nextTick()
updateBubbleSize()
updateTarget()
ensureTargetVisible()
})
watch(mode, () => {
+6 -3
View File
@@ -324,9 +324,12 @@ async fn initialize_state(app: tauri::AppHandle) -> api::Result<()> {
/// external game-directory root for the one-click StarLight install flow.
#[tauri::command]
fn get_launcher_root_dir() -> api::Result<String> {
let dir = theseus::default_game_dir_root().ok_or_else(|| {
let exe = std::env::current_exe().map_err(|error| {
theseus::Error::from(theseus::ErrorKind::FSError(error.to_string()))
})?;
let dir = exe.parent().ok_or_else(|| {
theseus::Error::from(theseus::ErrorKind::FSError(
"Could not determine a default game directory root".to_string(),
"Launcher executable has no parent directory".to_string(),
))
})?;
Ok(dir.to_string_lossy().into_owned())
@@ -336,7 +339,7 @@ fn get_launcher_root_dir() -> api::Result<String> {
fn get_update_channel(app: tauri::AppHandle) -> api::Result<String> {
let channel = read_update_channel_state(&app)?
.active_channel
.unwrap_or_else(|| theseus::default_update_channel().to_string());
.unwrap_or_else(|| "release".to_string());
match channel.as_str() {
"release" | "beta" => Ok(channel),
+2 -2
View File
@@ -33,13 +33,13 @@ pub use logger::start_logger;
pub use state::db::{
backup_current_app_db_for_update, beta_database_exists,
copy_database_between_channels, copy_release_database_to_beta,
current_app_database_path, default_update_channel,
current_app_database_path,
};
pub use state::{DirectoryInfo, State};
pub use storage::*;
pub use util::fetch::build_proxied_client;
pub use util::file_lock::{LockingProcess, get_locking_processes};
pub use util::platform::{default_game_dir_root, is_process_elevated};
pub use util::platform::is_process_elevated;
pub use util::proxy::{ProxyConfig, ProxyMode, ProxyTestResult};
pub use util::symlink::SymlinkCapability;
+1 -13
View File
@@ -304,18 +304,6 @@ async fn app_db_path(settings_dir: &Path) -> crate::Result<PathBuf> {
Ok(settings_dir.join(channel).join(LEGACY_APP_DB_FILE))
}
/// The update channel a fresh install should default to. Beta builds carry a
/// pre-release version (e.g. `1.0.2-beta7`), so their installers should start
/// on the Beta channel instead of silently falling back to Release.
pub fn default_update_channel() -> &'static str {
let version = env!("CARGO_PKG_VERSION");
if version.contains('-') {
"beta"
} else {
"release"
}
}
async fn read_update_channel(
settings_dir: &Path,
) -> crate::Result<&'static str> {
@@ -323,7 +311,7 @@ async fn read_update_channel(
let contents = match tokio::fs::read_to_string(&path).await {
Ok(contents) => contents,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
return Ok(default_update_channel());
return Ok("release");
}
Err(error) => return Err(error.into()),
};
-29
View File
@@ -1,6 +1,5 @@
//! Platform-related code
use daedalus::minecraft::{Os, OsRule};
use std::path::PathBuf;
/// (e.g. "Run as administrator" on Windows).
#[cfg(target_os = "windows")]
@@ -144,31 +143,3 @@ pub fn classpath_separator(java_arch: &str) -> &'static str {
Os::Windows | Os::WindowsArm64 => ";",
}
}
/// Root directory used as the default for one-click StarLight instance
/// installs. Portable builds live next to their executable, which is a fine
/// place for game data. System installs (e.g. a Linux `.deb` under `/usr/bin`)
/// do not, so fall back to the user's home directory there.
pub fn default_game_dir_root() -> Option<PathBuf> {
let exe_dir = std::env::current_exe().ok()?.parent()?.to_path_buf();
if cfg!(target_os = "linux") && is_system_install_dir(&exe_dir) {
return dirs::home_dir();
}
Some(exe_dir)
}
#[cfg(target_os = "linux")]
fn is_system_install_dir(path: &std::path::Path) -> bool {
const SYSTEM_PREFIXES: &[&str] = &[
"/usr/", "/opt/", "/snap/", "/bin/", "/sbin/", "/lib/", "/lib64/",
];
let display = path.to_string_lossy();
SYSTEM_PREFIXES
.iter()
.any(|prefix| display.starts_with(prefix))
}
#[cfg(not(target_os = "linux"))]
fn is_system_install_dir(_path: &std::path::Path) -> bool {
false
}