diff --git a/src/main/java/kst4contest/ApplicationConstants.java b/src/main/java/kst4contest/ApplicationConstants.java
index 5286e59..b595494 100644
--- a/src/main/java/kst4contest/ApplicationConstants.java
+++ b/src/main/java/kst4contest/ApplicationConstants.java
@@ -22,7 +22,7 @@ public class ApplicationConstants {
*/
public static final double APPLICATION_CURRENTVERSIONNUMBER = 1.42;
- public static final String VERSIONINFOURLFORUPDATES_KST4CONTEST = "https://do5amf.funkerportal.de/kst4ContestVersionInfo.xml";
+ public static final String VERSIONINFOURLFORUPDATES_KST4CONTEST = "https://kst4contest.hamradioonline.de/kst4ContestVersionInfo.xml";
public static final String VERSIONINFDOWNLOADEDLOCALFILE = "kst4ContestVersionInfo.xml";
public static final String STYLECSSFILE_DEFAULT_DAYLIGHT = "KST4ContestDefaultDay.css";
diff --git a/website/src/_data/version-history.xml b/website/src/_data/version-history.xml
new file mode 100644
index 0000000..2beccd4
--- /dev/null
+++ b/website/src/_data/version-history.xml
@@ -0,0 +1,382 @@
+
+
+
+
+ 1.41
+ 2026-07-01
+ Map view and path calculations
+
+Band and Frequency Detection per Chatmember, Band alert, Win-Test SKED Push via UDP,
+Win-Test Broadcast Defaults and SKed Workflow, Chat History at Startup, Station Map / Map View
+Local Leaflet /-TileProxy Backend, Maidenhead /-Grid Overlay, Path calculation, path profile,
+Reachability filters, NAC filters (grid sqaure marking)
+
+
+button housing, it works now on smaller screens / plus: detecting maximum screen size
+
+
+9A2HM reported that 23cm qsos with N1MM are not marked correctly, ist fixed
+
+
+
+
+ 1.40
+ 2026-02-16
+ Much changes and fixes
+
+- win-test support (not deactivable yet, beta)
+- QSO sniffer which filters changable callsigns lists messages to your pm window
+- Chatmember Score system. Each chatmember gets a score depending to antenna direction, activity time,
+ activity msgcount, positive signals, active bands, active frequencies, his sked direction (degrees),
+ self handed rate, sked-made-rate and some others
+- Chatmember Band and frequency recognition: now we list all active bands of each other chatmember
+- Band alert for logged stations
+- Sked reminder ALERT with automatic messages in 2+1 / 5+2+1 / 10+5+2+1 minutes
+- PSTRotator interface
+- AirPlane Timeline
+- Skedfail button in furtherinfo panel
+- Wintest logsynch interface can now be activated and deactivated in the preferences
+- load chat history on startup
+
+
+- added save option: ServerDNS / Port
+- added save option: activate/deactivate pstRotator interface
+- added save option: activate/deactivate wintest interface
+- added save option: callsign sniffer
+- added save option: boolean GUI_darkModeActiveByDefault = false
+- added AP notes at the internal DX cluster spots
+- Generic Autoanswer fires maximum once in 45 seconds per rx callsign
+
+
+- Userlist sort automatically on new member signon
+- Posonpill-messages now kills only exactly one Client instance (bugreport 9A2HM)
+- Crash of wtkst by disconnection of kst4Contest
+- wtKST crashes when kst4contest is running (many reporters)
+- wintest: logged per band excludes pre worked band (bugreport SM6VTZ)
+- PSTRotator SOCKET Close on DISCONNECT BUTTON works now
+- AirScout Map had not been updated (bugreport SM0RJV)
+- QTFDefault had been not saved correctly (bugreport SM0RJV)
+- Show path in AS button worked only with fixed server Strings (bugreport 9A2HM)
+- Version number had been displayed wrong
+- suffixes like /p, -2, etc. problem now fixed everywhere
+- Dark mode: passing for qrg fields fix
+
+
+- No more date display. Not needed. Saving space, only time is important
+
+
+
+ 1.31
+ 2025-12-13
+ Much changes and a hotfix
+
+- win-test support (not deactivable yet, beta)
+- pstRotator support (not deactivable yet, beta)
+- airScout fix (calculations depends to cpu load now)
+- QSO sniffer
+
+
+- DNS from www.on4kst.info to www.on4kst.org (hotfix, now configurable in preferences)
+
+
+- Endless loop in error case lets freeze the client
+- PSTRotator SOCKET Close on DISCONNECT BUTTON
+- Dark mode: passing for qrg fields fix
+
+
+
+
+ 1.266
+ 2025-10-03
+ Airscout interface did not work with multi-signons
+ nothing
+ nothing
+
+Fixed by using raw callsign (without -2 suffix etc.) when communicating with AirScout.
+Thank you very much for testing this, Kreso 9A2HM!
+
+
+
+
+ 1.265
+ 2025-09-28
+ Direction buttons stay now colored, if activated
+ nothing
+ nothing
+ The buttons stay now colored, if activated
+
+
+
+ 1.264
+ 2025-08-02
+ Changed the callsign recognition pattern for Simplelogfile
+ nothing
+ nothing
+
+Callsigns like S53CC, S51A etc. are now correctly marked as worked in SimpleLogFile.txt.
+(bugreport Boris, S53CC)
+
+
+
+
+ 1.263
+ 2025-06-08
+ Airscout communication and Loginname
+ nothing
+
+Only entries with QRB lower than max-QRB are sent to AirScout in 60s intervals (was 12s for all).
+
+
+- AS communication message count hugely reduced
+- Name in chat is now saveable
+- Fixed AS client name (was hard-wired to "KST")
+73 / DO5AMF
+
+
+
+
+ 1.262
+ 2025-05-21
+ Freezes caused by getting messages before user login should be fixed now
+ nothing
+ nothing
+
+ON4KST delivers messages of stations not yet logged in. That caused a freeze at the message
+processing engine which is now fixed.
+
+
+
+
+ 1.26
+ 2025-05
+ Login to multiple Channels via single signon / spend some colors
+
+1. UI: Dark mode. Switch in "Window -> use dark mode"
+2. Usage of two Chatcategories at the same time
+3. opposite station multi-callsign login-tagging
+73 / DO5AMF
+
+
+- Coloring mechanic of the software. Modify colors via css by yourself...
+
+
+- Station tagging fixed completely
+
+
+
+
+ 1.251
+ 2025-02
+ BUGFIX of 1.25, tnx Steve Clements!
+
+- Steve spotted a problem in udp broadcast spot info reading, its now fixed!
+73 / DO5AMF
+
+
+ - Station tagging
+
+
+
+ 1.25
+ 2025-02
+ Wishlist-time
+
+- New configuration Tab: Messagehandling (auto-answering, CQ qrg auto-answer)
+- Coloured lines: new pm rows appear in red, fade rainbow-like to white over 30s (tnx Gianluca)
+73 / DO5AMF
+
+
+
+- Users with suffixes like "-2 and -70" are now correctly marked as worked
+
+
+
+
+ 1.24
+ 2024-11
+ Wishlist-time
+
+- Button to show qrz.com profile of a selected station
+- Button to show qrzcq.com profile of a selected station
+
+
+
+
+
+
+ 1.23
+ 2024-10
+ DXCluster Server is now implemented
+
+- DXCluster Server (tnx OMAAO): KST4Contest now provides a DXCluster server to feed
+ your log client with station-reachable warnings.
+
+
+
+
+
+
+ 1.22
+ 2024-05
+ Increase usability, fixed AS button
+ - Variables (tnx OMAAO): MYLOCATORSHORT, MYQRGSHORT, QRZNAME
+ - Sendtext-field focus on callsign click
+
+- Worked-station-filter (tnx Gianluca): filter is now live
+- Chatters list sorting by QRB (tnx Alessandro): fixed to numeric sort
+- Airscout showpath button now maximizes AS and shows the path
+
+
+
+
+ 1.21
+ 2024-04
+ Increase usability
+
+
+- Window sizes and divider positions are now stored and restored on next startup
+- Filters section is now a flowpane for lower resolutions
+
+
+
+
+
+ 1.2
+ 2024-04
+ Increase usability
+
+- Selectable bands
+- Unworkable tags for each callsign per band
+- QTF-Arrow on the "show path in AS" button
+
+
+
+
+
+
+ 1.1
+ 2024-03
+ Increased workflow
+
+- Reachable warning function
+- Variables FIRSTAP and SECONDAP for predefined texts
+- Marking new connected stations (bold) and away-state (brackets)
+- Activity-timer since last message of a chatter
+
+ - QRB/QTF info in pm-window
+
+- Behaviour of messagefilter-radiobutton corrected (tnx Gian)
+- Error sending macros on selected stations in pm window (tnx Gian)
+
+ - boot sound
+
+
+ 1.0
+ 2024-02
+ First stable release
+
+- audio notification support
+- update-available warning
+- changelog displaying
+- known-bugs displaying
+- message filters for selected station
+- stationlist-filters (show by qtf / textstring, hide by qrb, qtf, workedstate)
+- 10 macros (textsnippets available by hitting strg+numberkey)
+
+
+
+
+
+
+ 0.9
+ 2024-02
+ Now the chat is disconnectable without closing it
+
+
+ disconnect-button, Textsnippets in CQ Window, "/CQ" at click on own sendtexts,
+setname "qrg" works now
+
+
+
+ 0.8
+ 2023-10
+ savesettings function and disconnect function added. MYCALL-Highlighting fixed
+ savesettings function, disconnect function
+
+ MYCALL-Highlighting
+
+
+
+ 0.7
+ 2023-07
+ Internal database for worked stations, shortcut and textsnippet editor
+ database, shortcut-editor, textsnipped-editor
+
+
+
+
+
+ 0.6
+ 2023-07
+ Added periodically sent beacon function
+ beacon to public channel
+
+
+
+
+
+ 0.5
+ 2023-02
+ Airscout interface, UDP interfaces for worked-station data and TRX info, shortcuts
+ Airscout-interface, packetreceiver for ucxlog, n1mm, qartest
+
+
+
+
+
+ 0.4
+ 2022-12
+ Worked info by logfile via universal pattern matching logfile interpreter
+ simple-logfile
+
+
+
+
+
+ 0.3
+ 2022-10
+ Added a GUI, chat was text based only
+ Graphical user interface
+
+
+
+
+
+ 0.2
+ 2022-10
+ Pattern matching system for frequency information, frequency collection per chatmember
+ frequency-extraction for every callsign
+
+
+
+
+
+ 0.15
+ 2022-09
+ Researched the Chatprotocol and developed the base communication with the chatserver
+
+
+
+
+
+
+ 0.1
+ 2022-09
+ Primary architecture of the chat, message bus system for handling the message queues
+
+
+
+
+
+
diff --git a/website/src/_data/version-legacy-sections.xml b/website/src/_data/version-legacy-sections.xml
new file mode 100644
index 0000000..cfeffe8
--- /dev/null
+++ b/website/src/_data/version-legacy-sections.xml
@@ -0,0 +1,131 @@
+
+
+
+
+
+ Cyclic update of own name in chat to actual QRG
+ 2024-02
+ open
+
+
+ Acoustic warnings
+ 2023-12
+ added
+
+
+ Airscout support
+ 2023-03
+ added
+
+
+ cyclic beacon messages in public chat
+ 2023-03
+ added
+
+
+
+
+
+stations like ol7c-7 and ol7c-2 will not be marked as worked if only "ol7c"
+is in the log. Need to interprete that. May ignore suffix starting with "-"
+ solved
+
+
+
+worked callsigns table is updated only once after connected to chat
+Should be available directly after program started.
+But Reset works all time
+ open
+
+
+ feature request: language-specific strings, maybe based to an xml file
+
+ open
+
+
+ feature request: Macros
+
+ added
+
+
+ feature request:
+ Automatic elimination of calls already connected from the log
+
+ added
+
+
+ feature request:
+ Limitation of users beyond a certain QRB
+
+ added
+
+
+ feature request:
+ User division for QTF perhaps settable based on the antenna lobe
+
+ added
+
+
+ feature request:
+ Notification of new incoming users
+ added
+
+
+ feature request:
+ Evidence of a call which was written but not responded to.
+
+ added
+
+
+ feature request:
+ direction based reachable-warnings:
+ if the chatclient detects a realised qso between two stations which both must have
+ directed their beams to my station, there should be an instant warning to me for
+ working at least one of the stations at the detected qrg
+ open
+
+
+ feature request:
+ implement ASShowpath message to airacout;
+ by doubleclicking a callsign in the chatmember list, the path and plane state should be
+ provided by airscout
+ open
+
+
+ feature request:
+ make chatlines copyable to the clipboard...
+ open
+
+
+ feature request:
+ adding an activity detection for sensing if another chatmembers alive-state
+ added
+
+
+ On sql error close the connection to make a clean exit
+ open
+
+
+ SETNAME /MYQRG in options menu doesnt work
+ solved
+
+
+ Disconnect button does not work (DL5ZK)
+ solved
+
+
+ Password-wrong error not displayed (DO5SA)
+ solved
+
+
+ /CQ isnt written on own PMs
+ solved
+
+
+ rightclick-textsnippets arent available in cq-table
+ solved
+
+
+
diff --git a/website/src/_data/versionInfo.js b/website/src/_data/versionInfo.js
new file mode 100644
index 0000000..77413ba
--- /dev/null
+++ b/website/src/_data/versionInfo.js
@@ -0,0 +1,183 @@
+const fs = require("fs");
+const path = require("path");
+
+const REPO = "praktimarc/kst4contest";
+const API = `https://api.github.com/repos/${REPO}`;
+const LABEL_MAP = { enhancement: "added", bug: "fixed" };
+
+async function githubGet(urlPath) {
+ const headers = { Accept: "application/vnd.github+json" };
+ if (process.env.GITHUB_TOKEN) {
+ headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
+ }
+ const res = await fetch(`${API}${urlPath}`, { headers });
+ if (!res.ok) {
+ throw new Error(`GitHub API ${urlPath} failed: ${res.status}`);
+ }
+ return res.json();
+}
+
+// UpdateChecker.java parses with Double.parseDouble() and
+// compares it against ApplicationConstants.APPLICATION_CURRENTVERSIONNUMBER,
+// which encodes patch releases by appending the patch digit(s) instead of a
+// third dot (e.g. tag v1.41.1 -> app version 1.411, v1.41.0 -> 1.41). See
+// commit 38ef50c ("...set to 1.41.1 for upcoming hotfix" -> 1.411).
+function toAppVersionNumber(tag) {
+ const cleaned = tag.replace(/^v/, "");
+ const [major, minor, ...rest] = cleaned.split(".");
+ if (rest.length === 0) return cleaned;
+ const patch = rest.join("");
+ return /^0*$/.test(patch) ? `${major}.${minor}` : `${major}.${minor}${patch}`;
+}
+
+function escapeXml(value) {
+ return String(value ?? "")
+ .replace(/&/g, "&")
+ .replace(//g, ">");
+}
+
+// Strips GitHub's auto-generated release-notes markdown noise (headings,
+// "Full Changelog" links, PR author attributions) down to plain text.
+function cleanReleaseBody(body) {
+ const lines = [];
+ for (let line of (body || "").split("\n")) {
+ if (/^#{1,3}\s/.test(line)) continue;
+ if (line.trim().startsWith("**Full Changelog**")) continue;
+ line = line.replace(/\s+by @\S+ in https:\/\/\S+/, "");
+ line = line.replace(/^\* /, "- ");
+ if (line.trim()) lines.push(line);
+ }
+ return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
+}
+
+// Groups closed issues (excluding PRs) in the (since, until] window by
+// label into "added" (enhancement) / "fixed" (bug) bullet lists.
+async function getIssuesForRelease(since, until) {
+ const buckets = { added: [], fixed: [] };
+ let page = 1;
+ for (;;) {
+ const items = await githubGet(`/issues?state=closed&per_page=100&page=${page}`);
+ if (!items.length) break;
+ for (const issue of items) {
+ if (issue.pull_request) continue;
+ const closed = issue.closed_at || "";
+ if (!(closed > since && closed <= until)) continue;
+ const labels = issue.labels.map((l) => l.name);
+ const bucket = Object.keys(LABEL_MAP).find((l) => labels.includes(l));
+ if (bucket) buckets[LABEL_MAP[bucket]].push(`- ${issue.title}`);
+ }
+ if (items.length < 100) break;
+ page++;
+ }
+ return { added: buckets.added.join("\n"), fixed: buckets.fixed.join("\n") };
+}
+
+// version-history.xml holds hand-written entries for releases
+// published before GitHub Releases existed; re-emitted verbatim here.
+function loadHistoryEntries() {
+ const historyPath = path.join(__dirname, "version-history.xml");
+ const xml = fs.readFileSync(historyPath, "utf-8");
+ return [...xml.matchAll(/[\s\S]*?<\/changeLog>/g)].map((m) => m[0]);
+}
+
+function historyEntryVersion(entryXml) {
+ const m = entryXml.match(/([\s\S]*?)<\/changedVersionNumber>/);
+ return m ? m[1].trim() : null;
+}
+
+// Static / sections carried over from the previous
+// hand-maintained XML on do5amf.funkerportal.de. UpdateChecker.java doesn't
+// parse these, kept only so the migration doesn't drop existing content.
+function loadLegacySections() {
+ const legacyPath = path.join(__dirname, "version-legacy-sections.xml");
+ const xml = fs.readFileSync(legacyPath, "utf-8");
+ const roadmap = xml.match(/[\s\S]*?<\/roadmap>/);
+ const bugsReported = xml.match(/[\s\S]*?<\/bugsReported>/);
+ return [roadmap ? roadmap[0] : null, bugsReported ? bugsReported[0] : null].filter(Boolean);
+}
+
+// Builds kst4ContestVersionInfo.xml (the update-checker feed read by
+// UpdateChecker.java) from GitHub releases + closed issues, falling back to
+// version-history.xml for releases that predate GitHub Releases.
+module.exports = async function () {
+ const fallback = '\n';
+
+ try {
+ const historyEntries = loadHistoryEntries();
+
+ const rawReleases = await githubGet("/releases?per_page=100");
+ const releases = rawReleases
+ .map((r) => ({
+ tagName: r.tag_name,
+ publishedAt: r.published_at || "",
+ name: r.name || r.tag_name,
+ body: r.body || "",
+ isPrerelease: r.prerelease
+ }))
+ .sort((a, b) => (a.publishedAt < b.publishedAt ? 1 : -1));
+
+ const stableReleases = releases.filter((r) => !r.isPrerelease);
+ const stable = stableReleases[0];
+ const ghVersions = new Set(stableReleases.map((r) => toAppVersionNumber(r.tagName)));
+
+ const parts = [];
+ parts.push('');
+ parts.push("");
+
+ const latestTag = stable ? stable.tagName : null;
+ const winFilename = latestTag ? `praktiKST-${latestTag}-windows-x64.zip` : "";
+
+ parts.push(" ");
+ if (stable) {
+ const latestIssues = await getIssuesForRelease("1970-01-01T00:00:00Z", stable.publishedAt);
+ parts.push(` ${escapeXml(toAppVersionNumber(latestTag))}`);
+ parts.push(" ");
+ parts.push(` ${escapeXml(latestIssues.added.slice(0, 300))}`);
+ parts.push(
+ ` https://github.com/${REPO}/releases/download/${latestTag}/${winFilename}`
+ );
+ }
+ parts.push(" ");
+
+ // UpdateChecker.parseUpdateXMLFile() never actually reads this field
+ // (setNeedUpdateResourcesSinceLastVersion is never called); the
+ // previous server always hardcoded "nothing" here, so we match that.
+ parts.push(" ");
+ parts.push(" nothing");
+ parts.push(" ");
+
+ for (const section of loadLegacySections()) {
+ parts.push(section);
+ }
+
+ for (let i = 0; i < stableReleases.length; i++) {
+ const rel = stableReleases[i];
+ const until = rel.publishedAt;
+ const since = stableReleases[i + 1] ? stableReleases[i + 1].publishedAt : "1970-01-01T00:00:00Z";
+ const issues = await getIssuesForRelease(since, until);
+
+ parts.push(" ");
+ parts.push(` ${escapeXml(toAppVersionNumber(rel.tagName))}`);
+ parts.push(` ${escapeXml(until.slice(0, 10))}`);
+ parts.push(` ${escapeXml(rel.name)}`);
+ parts.push(` ${escapeXml(issues.added)}`);
+ parts.push(` ${escapeXml(cleanReleaseBody(rel.body))}`);
+ parts.push(` ${escapeXml(issues.fixed)}`);
+ parts.push(" ");
+ parts.push(" ");
+ }
+
+ for (const entry of historyEntries) {
+ const version = historyEntryVersion(entry);
+ if (version && ghVersions.has(version)) continue;
+ parts.push(entry);
+ }
+
+ parts.push("");
+ return parts.join("\n");
+ } catch (err) {
+ console.warn(`[versionInfo] Could not generate version info XML, using empty fallback: ${err.message}`);
+ return fallback;
+ }
+};
diff --git a/website/src/version-info.njk b/website/src/version-info.njk
new file mode 100644
index 0000000..c4ba89e
--- /dev/null
+++ b/website/src/version-info.njk
@@ -0,0 +1,5 @@
+---
+permalink: /kst4ContestVersionInfo.xml
+eleventyExcludeFromCollections: true
+---
+{{ versionInfo | safe }}