1 Commits

Author SHA1 Message Date
9644de3ff5 Fixed some UI issues with updater. 2026-09-10 00:13:05 +02:00
6 changed files with 214 additions and 73 deletions

View File

@@ -1,6 +1,6 @@
# PristonTale EU for Linux
Version 0.9.8 installs, updates, repairs, and launches PristonTale EU through a
Version 0.9.11 installs, updates, repairs, and launches PristonTale EU through a
native Linux dashboard. Game files come directly from the publisher and are not
included in this archive.
@@ -15,11 +15,11 @@ Then run:
```text
openssl pkey -pubin -in launcher-update-public.pem -outform DER | sha256sum
openssl dgst -sha256 -verify launcher-update-public.pem \
-signature pristontale-eu-linux-installer-v0-9-8.tar.gz.sig \
pristontale-eu-linux-installer-v0-9-8.tar.gz
sha256sum -c pristontale-eu-linux-installer-v0-9-8.tar.gz.sha256
tar -xzf pristontale-eu-linux-installer-v0-9-8.tar.gz
cd pristontale-eu-linux-installer-v0-9-8
-signature pristontale-eu-linux-installer-v0-9-11.tar.gz.sig \
pristontale-eu-linux-installer-v0-9-11.tar.gz
sha256sum -c pristontale-eu-linux-installer-v0-9-11.tar.gz.sha256
tar -xzf pristontale-eu-linux-installer-v0-9-11.tar.gz
cd pristontale-eu-linux-installer-v0-9-11
./install.sh
```

View File

@@ -4,4 +4,4 @@ from .paths import AppPaths
__all__ = ["AppPaths", "__version__"]
__version__ = "0.9.8"
__version__ = "0.9.11"

View File

@@ -7,7 +7,6 @@ from typing import Any, Callable
from ...paths import AppPaths
from ...self_update import (
LauncherRemoteHistory,
LauncherRemoteRelease,
LauncherUpdateCheck,
LauncherUpdateHistory,
inspect_launcher_update_history,
@@ -52,6 +51,46 @@ def release_notes_preview(markdown: str | None) -> str:
return preview
def update_version_heading(
running_version: str,
latest_version: str,
channel: str,
*,
update_available: bool,
) -> str:
"""Describe update versions without confusing an older channel head as current."""
if update_available:
return f"Update: {running_version} → {latest_version}"
return (
f"Current version: {running_version}\n"
f"Latest {channel.title()} release: {latest_version}"
)
def release_history_state(
release_status: str,
*,
is_running: bool,
local_active: bool,
local_retained: bool,
is_older: bool,
) -> str:
"""Label running and installed releases without treating them as synonymous."""
if is_running:
return "Current"
if local_active:
return "Installed"
if release_status != "active":
return release_status.title()
if local_retained:
return "Retained locally"
if is_older:
return "Available rollback"
return "Published"
class LauncherUpdateDialog:
"""Own the update window and expose small refresh/error entry points."""
@@ -97,6 +136,8 @@ class LauncherUpdateDialog:
self._history: Any = None
self._history_filter_value: Any = None
self._history_items: dict[str, tuple[str, str, str | None, bool]] = {}
self._release_notes_frame: Any = None
self._release_notes_text: Any = None
self._release_notes_button: Any = None
self._check_button: Any = None
self._rollback_button: Any = None
@@ -120,7 +161,7 @@ class LauncherUpdateDialog:
dialog.withdraw()
self._window = dialog
dialog.title("PristonTale EU — Launcher Updates")
dialog.resizable(False, False)
dialog.resizable(False, True)
dialog.configure(background="#0c0908")
dialog.transient(self._root)
self._apply_window_icon(dialog)
@@ -201,6 +242,53 @@ class LauncherUpdateDialog:
sticky="e",
padx=(12, 0),
)
self._release_notes_frame = ttk.Frame(
overview,
style="SettingsCard.TFrame",
)
self._release_notes_frame.grid(
row=2,
column=0,
columnspan=2,
sticky="ew",
pady=(10, 0),
)
self._release_notes_frame.columnconfigure(0, weight=1)
ttk.Label(
self._release_notes_frame,
text="WHAT'S NEW",
style="SettingsText.TLabel",
).grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 4))
self._release_notes_text = tk.Text(
self._release_notes_frame,
name="launcher_update_release_notes",
width=1,
height=3,
wrap="word",
background="#100d0c",
foreground="#c8b8a8",
selectbackground="#6a482d",
selectforeground="#ffffff",
font=("Sans", 8),
borderwidth=0,
highlightthickness=0,
padx=8,
pady=6,
state="disabled",
takefocus=False,
)
release_notes_scrollbar = ttk.Scrollbar(
self._release_notes_frame,
orient="vertical",
style="News.Vertical.TScrollbar",
command=self._release_notes_text.yview,
)
self._release_notes_text.configure(
yscrollcommand=release_notes_scrollbar.set,
)
self._release_notes_text.grid(row=1, column=0, sticky="ew")
release_notes_scrollbar.grid(row=1, column=1, sticky="ns", padx=(6, 0))
self._release_notes_frame.grid_remove()
history_card = ttk.LabelFrame(
content,
@@ -244,7 +332,7 @@ class LauncherUpdateDialog:
history_card,
columns=("version", "channel", "state", "installed"),
show="headings",
height=6,
height=4,
style="Update.Treeview",
selectmode="browse",
)
@@ -333,8 +421,8 @@ class LauncherUpdateDialog:
self._check_again()
dialog.update_idletasks()
updates_width, updates_height = fitted_window_size(
760,
620,
820,
680,
scale=self._ui_scale,
screen_width=dialog.winfo_screenwidth(),
screen_height=dialog.winfo_screenheight(),
@@ -365,6 +453,8 @@ class LauncherUpdateDialog:
assert self._status_value is not None
assert self._details_value is not None
assert self._history is not None
assert self._release_notes_frame is not None
assert self._release_notes_text is not None
assert self._release_notes_button is not None
assert self._check_button is not None
assert self._rollback_button is not None
@@ -381,10 +471,11 @@ class LauncherUpdateDialog:
if check is None:
self._status_value.set("Checking the signed update channel…")
self._details_value.set(
"Installed launcher "
"Running launcher "
+ self._launcher_version
+ " · release information pending"
)
self._set_release_notes_preview("")
self._install_button.configure(state="disabled")
self._refresh_history_actions(busy=busy)
return
@@ -400,13 +491,14 @@ class LauncherUpdateDialog:
for release in (
remote_history.releases if remote_history is not None else ()
)
if release.version == check.current_version and release.status != "active"
if release.version == self._launcher_version
and release.status != "active"
),
None,
)
if installed_status is not None:
status_prefix += (
f"Warning: installed launcher {check.current_version} is "
f"Warning: running launcher {self._launcher_version} is "
f"{installed_status}; install the next active eligible release.\n"
)
self._status_value.set(status_prefix + check.reason)
@@ -418,18 +510,24 @@ class LauncherUpdateDialog:
if check.manifest.channel == "prerelease"
else ""
)
summary_line = f"{release_summary}\n" if not notes_preview else ""
version_line = update_version_heading(
self._launcher_version,
check.latest_version,
check.manifest.channel,
update_available=check.available,
)
details = (
f"{check.current_version} → {check.latest_version}\n"
f"{version_line}\n"
f"{release_title}{prerelease_label}\n"
f"{release_summary}\n"
f"{summary_line}"
f"Channel: {check.manifest.channel.title()} · "
f"{format_bytes(check.manifest.archive_size)} · "
f"published {check.manifest.published_at} · "
f"checked {check.checked_at or 'time unavailable'}"
)
if notes_preview:
details += f"\n\nWhat's new:\n{notes_preview}"
self._details_value.set(details)
self._set_release_notes_preview(notes_preview)
self._install_button.configure(
state=(
"normal"
@@ -442,6 +540,20 @@ class LauncherUpdateDialog:
)
self._refresh_history_actions(busy=busy)
def _set_release_notes_preview(self, preview: str) -> None:
assert self._release_notes_frame is not None
assert self._release_notes_text is not None
self._release_notes_text.configure(state="normal")
self._release_notes_text.delete("1.0", "end")
if preview:
self._release_notes_text.insert("1.0", preview)
self._release_notes_text.configure(state="disabled")
self._release_notes_text.yview_moveto(0.0)
if preview:
self._release_notes_frame.grid()
else:
self._release_notes_frame.grid_remove()
def _populate_history(self, *, busy: bool) -> None:
assert self._history is not None
assert self._history_filter_value is not None
@@ -461,16 +573,22 @@ class LauncherUpdateDialog:
else ()
)
}
current_version = (
installed_current_version = (
self._installed_history.current_version
if self._installed_history is not None
and self._installed_history.current_version is not None
else self._launcher_version
)
try:
current_semantic = SemanticVersion.parse(current_version)
installed_current_semantic = SemanticVersion.parse(
installed_current_version
)
except ReleaseModelError:
current_semantic = None
installed_current_semantic = None
try:
running_channel = SemanticVersion.parse(self._launcher_version).channel
except ReleaseModelError:
running_channel = "unknown"
filter_name = str(self._history_filter_value.get()).casefold()
def channel_visible(channel: str) -> bool:
@@ -478,20 +596,27 @@ class LauncherUpdateDialog:
remote_history = self._get_remote_history()
remote_versions: set[str] = set()
displayed_versions: set[str] = set()
if remote_history is not None:
for index, release in enumerate(remote_history.releases):
if not channel_visible(release.channel):
continue
remote_versions.add(release.version)
displayed_versions.add(release.version)
local = installed.get(release.version)
is_older = (
current_semantic is not None
and SemanticVersion.parse(release.version) < current_semantic
installed_current_semantic is not None
and SemanticVersion.parse(release.version)
< installed_current_semantic
)
can_rollback = release.status == "active" and is_older
state = self._remote_release_state(
release,
local_active=bool(local is not None and local.active),
local_active = bool(local is not None and local.active)
can_rollback = (
release.status == "active" and is_older and not local_active
)
state = release_history_state(
release.status,
is_running=release.version == self._launcher_version,
local_active=local_active,
local_retained=local is not None,
is_older=is_older,
)
@@ -517,14 +642,23 @@ class LauncherUpdateDialog:
for index, release in enumerate(self._installed_history.releases):
if release.version in remote_versions or not channel_visible(release.channel):
continue
displayed_versions.add(release.version)
try:
is_older = (
current_semantic is not None
and SemanticVersion.parse(release.version) < current_semantic
installed_current_semantic is not None
and SemanticVersion.parse(release.version)
< installed_current_semantic
)
except ReleaseModelError:
is_older = False
state = "Current" if release.active else "Retained locally"
state = release_history_state(
"active",
is_running=release.version == self._launcher_version,
local_active=release.active,
local_retained=True,
is_older=is_older,
)
can_rollback = is_older and not release.active
iid = f"local:{index}:{release.version}"
self._history.insert(
"",
@@ -541,7 +675,29 @@ class LauncherUpdateDialog:
release.version,
release.channel,
None,
is_older,
can_rollback,
)
if (
self._launcher_version not in displayed_versions
and channel_visible(running_channel)
):
iid = f"running:{self._launcher_version}"
self._history.insert(
"",
0,
iid=iid,
values=(
self._launcher_version,
running_channel.title(),
"Current",
"Running now",
),
)
self._history_items[iid] = (
self._launcher_version,
running_channel,
None,
False,
)
if not self._history.get_children():
iid = "placeholder"
@@ -567,24 +723,6 @@ class LauncherUpdateDialog:
self._history.focus(selected_iid)
self._refresh_history_actions(busy=busy)
@staticmethod
def _remote_release_state(
release: LauncherRemoteRelease,
*,
local_active: bool,
local_retained: bool,
is_older: bool,
) -> str:
if local_active:
return "Current"
if release.status != "active":
return release.status.title()
if local_retained:
return "Retained locally"
if is_older:
return "Available rollback"
return "Published"
def _history_selected(self, _event: object | None = None) -> None:
self._refresh_history_actions(busy=self._is_busy())
@@ -621,6 +759,7 @@ class LauncherUpdateDialog:
assert self._check_button is not None
self._status_value.set("The signed update channel could not be checked.")
self._details_value.set(message)
self._set_release_notes_preview("")
self._check_button.configure(state="normal")
def _check_again(self) -> None:
@@ -630,6 +769,7 @@ class LauncherUpdateDialog:
assert self._install_button is not None
self._status_value.set("Checking the signed update channel…")
self._details_value.set("Authenticating the selected channel manifests and signatures.")
self._set_release_notes_preview("")
self._check_button.configure(state="disabled")
self._install_button.configure(state="disabled")
self._request_check()
@@ -637,8 +777,9 @@ class LauncherUpdateDialog:
def _view_release_notes(self) -> None:
selected = self._selected_history_item()
if selected is not None:
if selected[2] is not None:
self._open_article(selected[2])
notes_url = selected[2]
if notes_url is not None:
self._open_article(notes_url)
return
check = self._get_check()
if check is not None and check.manifest.notes_url is not None:

View File

@@ -1,6 +1,6 @@
[project]
name = "PTEU-Linux-Launcher"
version = "0.9.8"
version = "0.9.11"
description = "PristonTale EU Linux installer, clean launcher, and diagnostic harness"
requires-python = ">=3.11"
dependencies = []

View File

@@ -2,16 +2,16 @@
"application": "PristonTale-EU",
"archive": {
"layout": "wrapped",
"name": "PristonTale-EU-0.9.10.tar.gz",
"sha256": "76df8942d2f0b3b533eb967f8a2379b2a7bdd44fd58305d0f8bf061778fbd9ea",
"size": 1473687,
"url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/download/v0.9.10/PristonTale-EU-0.9.10.tar.gz"
"name": "PristonTale-EU-0.9.11.tar.gz",
"sha256": "60b13d6fbc8fe27714c7fe1a65fa73785a13962d96d03decfac1aa62c4e3f6db",
"size": 1474010,
"url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/download/v0.9.11/PristonTale-EU-0.9.11.tar.gz"
},
"archive_sha256": "76df8942d2f0b3b533eb967f8a2379b2a7bdd44fd58305d0f8bf061778fbd9ea",
"archive_size": 1473687,
"archive_url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/download/v0.9.10/PristonTale-EU-0.9.10.tar.gz",
"archive_sha256": "60b13d6fbc8fe27714c7fe1a65fa73785a13962d96d03decfac1aa62c4e3f6db",
"archive_size": 1474010,
"archive_url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/download/v0.9.11/PristonTale-EU-0.9.11.tar.gz",
"channel": "stable",
"commit": "27ed039f327d4d14d1d04717e0fd4f20ad7b7dc9",
"commit": "a362b0bb8e2bf74db9075c5f58db5236b1dc98f6",
"compatibility": {
"config_schema": 1,
"manifest_schema": 1,
@@ -36,7 +36,7 @@
},
{
"path": "ptlinux/__init__.py",
"sha256": "a80db24319120aa083e8abf0e099bacc8958bc04e59a2ac17ee270ae0f7094a3",
"sha256": "b4eaa89165a506ff593083dd32ad30fefe1d8ec7d09af67b45b29a3d13599df0",
"size": 152
},
{
@@ -306,8 +306,8 @@
},
{
"path": "ptlinux/ui/components/update_dialog.py",
"sha256": "af5775b07b6872b5e5f465deb8d9f583ddfeeae1df79a126fb2d2fe967bb3d06",
"size": 22005
"sha256": "1defc85b4cf30f15fa1c30bcfd0cbf961072ba2d2ab8a681ef981e34ef3598d4",
"size": 24138
},
{
"path": "ptlinux/ui/dashboard.py",
@@ -371,21 +371,21 @@
},
{
"path": "release.json",
"sha256": "e1c3a4501457050f00ed98d254b83521f173bccb91cb6176bff2ca89aab99adc",
"sha256": "afc50744e1d2cd13c3062b6160cd21a0b25870da81270b75b0a35940ca33b23b",
"size": 223
}
],
"minimum_updater_version": "0.5.0",
"notes_url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/tag/v0.9.10",
"published_at": "2026-09-09T21:01:49Z",
"notes_url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/tag/v0.9.11",
"published_at": "2026-09-09T21:12:47Z",
"release": {
"notes_markdown": "# PristonTale EU Launcher 0.9.10\n\nRestores automatic updates for existing launchers and completes the transition to flat release archives.\n\n## Changes\n\n- Updated to new release v0.9.9 (`27ed039`)\n- Added better support for kubuntu and fixed some issues with telemetry. (`290d1a5`)\n- Updated diagnostic message. (`6e62191`)\n- Updated the update system to support multi channels (stable / pre-release). (`5770969`)\n- Released a new version, 0.9.8. (`2a53a59`)\n- Fixed more issues with Cinnamon window resizing and popups. (`76bef94`)\n- Fixed issues with telemetry. (`1a120bb`)\n- Released new stable version 0.9.7. (`108ea94`)\n- Added a fix for X11 / Cinnamon window popups and maximize button not visible. (`1cfe862`)\n- Added telemetry system to get information about issues. (`f06fc2e`)\n- Upload files to \"updates\" (`a1ff479`)\n- Delete updates/stable.json.sig (`6eea3b2`)\n- Update updates/stable.json (`ee9bb06`)\n- Fixed issues with gamescope not properly launching. (`bea9d6f`)\n\n---\n\nVersion: `0.9.10` \nChannel: `stable` \nTag: `v0.9.10` \nSource commit: `27ed039f327d4d14d1d04717e0fd4f20ad7b7dc9`\n",
"release_notes_url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/tag/v0.9.10",
"summary": "Restores automatic updates for existing launchers and completes the transition to flat release archives.",
"title": "PristonTale EU Launcher 0.9.10"
"notes_markdown": "# PristonTale EU Launcher 0.9.11\n\nFixes the update window layout so release notes no longer cover version history or controls.\n\n---\n\nVersion: `0.9.11` \nChannel: `stable` \nTag: `v0.9.11` \nSource commit: `a362b0bb8e2bf74db9075c5f58db5236b1dc98f6`\n",
"release_notes_url": "https://git.m4ko.net/m4ko/pristontale-eu-linux/releases/tag/v0.9.11",
"summary": "Fixes the update window layout so release notes no longer cover version history or controls.",
"title": "PristonTale EU Launcher 0.9.11"
},
"schema": 1,
"status": "active",
"tag": "v0.9.10",
"version": "0.9.10"
"tag": "v0.9.11",
"version": "0.9.11"
}

Binary file not shown.