3 Commits

52 changed files with 2054 additions and 1427 deletions
@@ -24,6 +24,7 @@ import kst4contest.logic.PriorityCalculator;
import kst4contest.model.*; import kst4contest.model.*;
import kst4contest.test.MockKstServer; import kst4contest.test.MockKstServer;
import kst4contest.utils.PlayAudioUtils; import kst4contest.utils.PlayAudioUtils;
import kst4contest.locatorUtils.Location;
import kst4contest.view.Kst4ContestApplication; import kst4contest.view.Kst4ContestApplication;
import java.io.*; import java.io.*;
@@ -34,6 +35,7 @@ import java.util.function.Predicate;
/** /**
* *
* Central Chat kst4contest.controller. Instantiate only one time per category of kst Chat. * Central Chat kst4contest.controller. Instantiate only one time per category of kst Chat.
@@ -318,33 +320,29 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
EnumSet<Band> offered = EnumSet.noneOf(Band.class); EnumSet<Band> offered = EnumSet.noneOf(Band.class);
synchronized (getLst_chatMemberList()) { for (ChatMember cm : findActiveChatMembersByRawCall(callRaw)) {
for (ChatMember cm : getLst_chatMemberList()) { if (cm == null || cm.getCallSignRaw() == null) continue;
if (cm == null || cm.getCallSignRaw() == null) continue;
if (!normalizeCallRaw(cm.getCallSignRaw()).equals(callRaw)) continue;
Map<Band, ChatMember.ActiveFrequencyInfo> map = cm.getKnownActiveBands(); Map<Band, ChatMember.ActiveFrequencyInfo> map = cm.getKnownActiveBands();
if (map == null || map.isEmpty()) continue; if (map == null || map.isEmpty()) continue;
for (Map.Entry<Band, ChatMember.ActiveFrequencyInfo> e : map.entrySet()) { for (Map.Entry<Band, ChatMember.ActiveFrequencyInfo> e : map.entrySet()) {
if (e.getKey() == null || e.getValue() == null) continue; if (e.getKey() == null || e.getValue() == null) continue;
long age = nowMs - e.getValue().timestampEpoch;
if (age >= 0 && age <= maxAgeMs) {
offered.add(e.getKey());
}
if (DEBUG_BAND_UPGRADE_HINT) {
System.out.println("[BandUpgradeHint] history call=" + callRaw
+ " band=" + e.getKey()
+ " freq=" + e.getValue().frequency
+ " ageMs=" + age);
}
long age = nowMs - e.getValue().timestampEpoch;
if (age >= 0 && age <= maxAgeMs) {
offered.add(e.getKey());
}
if (DEBUG_BAND_UPGRADE_HINT) {
System.out.println("[BandUpgradeHint] history call=" + callRaw
+ " band=" + e.getKey()
+ " freq=" + e.getValue().frequency
+ " ageMs=" + age);
} }
} }
} }
return offered; return offered;
} }
@@ -356,21 +354,19 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
EnumSet<Band> worked = EnumSet.noneOf(Band.class); EnumSet<Band> worked = EnumSet.noneOf(Band.class);
synchronized (getLst_chatMemberList()) { for (ChatMember cm : findActiveChatMembersByRawCall(callRaw)) {
for (ChatMember cm : getLst_chatMemberList()) { if (cm == null || cm.getCallSignRaw() == null) continue;
if (cm == null || cm.getCallSignRaw() == null) continue;
if (!normalizeCallRaw(cm.getCallSignRaw()).equals(callRaw)) continue;
if (cm.isWorked144()) worked.add(Band.B_144); if (cm.isWorked144()) worked.add(Band.B_144);
if (cm.isWorked432()) worked.add(Band.B_432); if (cm.isWorked432()) worked.add(Band.B_432);
if (cm.isWorked1240()) worked.add(Band.B_1296); if (cm.isWorked1240()) worked.add(Band.B_1296);
if (cm.isWorked2300()) worked.add(Band.B_2320); if (cm.isWorked2300()) worked.add(Band.B_2320);
if (cm.isWorked3400()) worked.add(Band.B_3400); if (cm.isWorked3400()) worked.add(Band.B_3400);
if (cm.isWorked5600()) worked.add(Band.B_5760); if (cm.isWorked5600()) worked.add(Band.B_5760);
if (cm.isWorked10G()) worked.add(Band.B_10G); if (cm.isWorked10G()) worked.add(Band.B_10G);
if (cm.isWorked24G()) worked.add(Band.B_24G); // optional, only if your Band enum supports it if (cm.isWorked24G()) worked.add(Band.B_24G);
}
} }
return worked; return worked;
} }
@@ -645,7 +641,8 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
// rotatorClient. // rotatorClient.
this.lst_chatMemberList.clear();; // this.lst_chatMemberList.clear();;
this.clearActiveChatMembers();
this.lst_clusterMemberList.clear(); this.lst_clusterMemberList.clear();
this.setDisconnected(true); this.setDisconnected(true);
@@ -713,7 +710,7 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
} }
} else if (action.equals(ApplicationConstants.DISCSTRING_DISCONNECTONLY)){ } else if (action.equals(ApplicationConstants.DISCSTRING_DISCONNECTONLY)){
this.lst_chatMemberList.clear();; this.clearActiveChatMembers();
this.lst_clusterMemberList.clear(); this.lst_clusterMemberList.clear();
@@ -959,16 +956,9 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
if (targetCallsignRaw == null || targetCallsignRaw.isBlank()) { if (targetCallsignRaw == null || targetCallsignRaw.isBlank()) {
return null; return null;
} }
String normalizedTargetCall = normalizeCallRaw(targetCallsignRaw);
synchronized (getLst_chatMemberList()) { List<ChatMember> matchingMembers = findActiveChatMembersByRawCall(targetCallsignRaw);
for (ChatMember member : getLst_chatMemberList()) { return matchingMembers.isEmpty() ? null : matchingMembers.get(0);
if (member == null || member.getCallSignRaw() == null) continue;
if (normalizeCallRaw(member.getCallSignRaw()).equals(normalizedTargetCall)) {
return member;
}
}
}
return null;
} }
private String resolveSkedTargetLocator(String targetCallsignRaw) { private String resolveSkedTargetLocator(String targetCallsignRaw) {
@@ -976,22 +966,43 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
return null; return null;
} }
String normalizedTargetCall = normalizeCallRaw(targetCallsignRaw); for (ChatMember member : findActiveChatMembersByRawCall(targetCallsignRaw)) {
synchronized (getLst_chatMemberList()) { String locator = member.getQra();
for (ChatMember member : getLst_chatMemberList()) { if (locator != null && !locator.isBlank()) {
if (member == null || member.getCallSignRaw() == null) continue; return locator.trim().toUpperCase(Locale.ROOT);
if (!normalizeCallRaw(member.getCallSignRaw()).equals(normalizedTargetCall)) continue;
String locator = member.getQra();
if (locator != null && !locator.isBlank()) {
return locator.trim().toUpperCase(Locale.ROOT);
}
} }
} }
return null; return null;
} }
// private ChatMember resolveSkedTargetMember(String targetCallsignRaw) {
// if (targetCallsignRaw == null || targetCallsignRaw.isBlank()) {
// return null;
// }
//
// List<ChatMember> matchingMembers = findActiveChatMembersByRawCall(targetCallsignRaw);
// return matchingMembers.isEmpty() ? null : matchingMembers.get(0);
//
// }
//
// private String resolveSkedTargetLocator(String targetCallsignRaw) {
// if (targetCallsignRaw == null || targetCallsignRaw.isBlank()) {
// return null;
// }
//
// String normalizedTargetCall = normalizeCallRaw(targetCallsignRaw);
//
// for (ChatMember member : findActiveChatMembersByRawCall(targetCallsignRaw)) {
// String locator = member.getQra();
// if (locator != null && !locator.isBlank()) {
// return locator.trim().toUpperCase(Locale.ROOT);
// }
// }
//
// return null;
// }
public StationMetricsService getStationMetricsService() { public StationMetricsService getStationMetricsService() {
return stationMetricsService; return stationMetricsService;
} }
@@ -1014,10 +1025,14 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
return new HashMap<>(lastInboundCategoryByCallSignRaw); return new HashMap<>(lastInboundCategoryByCallSignRaw);
} }
// public List<ChatMember> snapshotChatMembers() {
// synchronized (getLst_chatMemberList()) {
// return new ArrayList<>(getLst_chatMemberList());
// }
// }
public List<ChatMember> snapshotChatMembers() { public List<ChatMember> snapshotChatMembers() {
synchronized (getLst_chatMemberList()) { return new ArrayList<>(activeChatMembersByCallAndCategory.values());
return new ArrayList<>(getLst_chatMemberList());
}
} }
public List<ContestSked> snapshotActiveSkeds() { public List<ContestSked> snapshotActiveSkeds() {
@@ -1126,13 +1141,27 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
private ObservableList<ChatMember> chatMemberList = FXCollections.observableArrayList(); // List of active stations private ObservableList<ChatMember> chatMemberList = FXCollections.observableArrayList(); // List of active stations
private ObservableList<ChatMember> lst_chatMemberList = FXCollections.synchronizedObservableList(chatMemberList); // List
// of active stn in chat // of active stn in chat
private FilteredList<ChatMember> lst_chatMemberListFiltered = new FilteredList<ChatMember>(chatMemberList); private FilteredList<ChatMember> lst_chatMemberListFiltered = new FilteredList<ChatMember>(chatMemberList);
private SortedList<ChatMember> lst_chatMemberSortedFilteredList = new SortedList<ChatMember>(lst_chatMemberListFiltered); private SortedList<ChatMember> lst_chatMemberSortedFilteredList = new SortedList<ChatMember>(lst_chatMemberListFiltered);
private ObservableList<ChatMember> lst_chatMemberList = FXCollections.synchronizedObservableList(chatMemberList); // List
private ObservableList<Predicate<ChatMember>> lst_chatMemberListFilterPredicates = FXCollections.observableArrayList(); private ObservableList<Predicate<ChatMember>> lst_chatMemberListFilterPredicates = FXCollections.observableArrayList();
private ObservableList<ClusterMessage> lst_clusterMemberList = FXCollections.observableArrayList(); private ObservableList<ClusterMessage> lst_clusterMemberList = FXCollections.observableArrayList();
/*
* Thread-safe active-member model.
*
* MessageBusManagementThread must not use the JavaFX TableView backing list as
* its primary data model. Otherwise every add/remove fires FilteredList,
* SortedList and TableView selection listeners on the MessageBus thread and JavaFX
* can throw "Not on FX application thread" or internal listener NPEs.
*
* This map is the worker-thread safe source of truth. The ObservableList below
* remains a UI mirror and is mutated only on the JavaFX application thread.
*/
private final java.util.concurrent.ConcurrentMap<String, ChatMember> activeChatMembersByCallAndCategory =
new java.util.concurrent.ConcurrentHashMap<>();
/* /*
* Message table update buffers. * Message table update buffers.
@@ -1161,6 +1190,254 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
// ****************************************************************************************************************************************** // ******************************************************************************************************************************************
/**
* Executes UI-bound work on the JavaFX application thread.
*
* Worker threads may call this safely. If the caller is already on the FX
* thread, the action is executed immediately to preserve ordering.
*/
private void runOnFxThread(Runnable action) {
if (action == null) {
return;
}
if (Platform.isFxApplicationThread()) {
action.run();
} else {
Platform.runLater(action);
}
}
/**
* Builds the active-member key. The raw/base callsign alone is not enough
* because the same station can be logged into multiple ON4KST categories at
* the same time. Therefore the category number is part of the key.
*/
private String buildActiveChatMemberKey(String callSign, ChatCategory category) {
String normalizedCallsign = ChatMember.normalizeCallSignToBaseCallSign(callSign);
if (normalizedCallsign == null || normalizedCallsign.isBlank()) {
return null;
}
int categoryNumber = category == null ? -1 : category.getCategoryNumber();
return normalizedCallsign.trim().toUpperCase(Locale.ROOT) + "|" + categoryNumber;
}
private String buildActiveChatMemberKey(ChatMember member) {
if (member == null) {
return null;
}
String callSign = member.getCallSignRaw() != null ? member.getCallSignRaw() : member.getCallSign();
return buildActiveChatMemberKey(callSign, member.getChatCategory());
}
/**
* Adds or replaces an active chat member in the worker-thread model and mirrors
* that change to the JavaFX list. This is the only supported path for ON4KST
* user-enter events.
*/
public void addOrUpdateActiveChatMember(ChatMember member) {
String key = buildActiveChatMemberKey(member);
if (key == null) {
return;
}
activeChatMembersByCallAndCategory.put(key, member);
runOnFxThread(() -> {
int existingIndex = findChatMemberIndexInUiListByKey(key);
if (existingIndex >= 0) {
lst_chatMemberList.set(existingIndex, member);
} else {
lst_chatMemberList.add(member);
}
});
}
/**
* Removes an active chat member from the worker-thread model and from the UI
* mirror. Removal from the ObservableList is always performed on the FX thread
* because the list is bound to FilteredList/SortedList/TableView.
*/
public boolean removeActiveChatMember(ChatMember member) {
String key = buildActiveChatMemberKey(member);
if (key == null) {
return false;
}
ChatMember removedMember = activeChatMembersByCallAndCategory.remove(key);
runOnFxThread(() -> lst_chatMemberList.removeIf(currentMember -> key.equals(buildActiveChatMemberKey(currentMember))));
return removedMember != null;
}
/**
* Clears the active-member model and the JavaFX UI mirror. Use this instead of
* getLst_chatMemberList().clear() from timers or worker threads.
*/
public void clearActiveChatMembers() {
activeChatMembersByCallAndCategory.clear();
runOnFxThread(() -> lst_chatMemberList.clear());
}
/**
* Resolves a member from the thread-safe active model. This avoids reading the
* TableView backing list from MessageBusManagementThread.
*/
public ChatMember findActiveChatMember(ChatMember lookForThis) {
String key = buildActiveChatMemberKey(lookForThis);
return key == null ? null : activeChatMembersByCallAndCategory.get(key);
}
public ChatMember findActiveChatMember(String callSign, ChatCategory category) {
String key = buildActiveChatMemberKey(callSign, category);
return key == null ? null : activeChatMembersByCallAndCategory.get(key);
}
public int getActiveChatMemberCount() {
return activeChatMembersByCallAndCategory.size();
}
/**
* Returns all active category variants of a callsign. This is used when a QRG or
* band hint should be propagated from one category instance to the same station
* in another category.
*/
public List<ChatMember> findActiveChatMembersByRawCall(String callSignRaw) {
String normalizedCallsign = ChatMember.normalizeCallSignToBaseCallSign(callSignRaw);
if (normalizedCallsign == null || normalizedCallsign.isBlank()) {
return Collections.emptyList();
}
String normalizedKeyPart = normalizedCallsign.trim().toUpperCase(Locale.ROOT);
List<ChatMember> matchingMembers = new ArrayList<>();
for (ChatMember member : activeChatMembersByCallAndCategory.values()) {
if (member == null) {
continue;
}
String memberCall = member.getCallSignRaw() != null ? member.getCallSignRaw() : member.getCallSign();
String normalizedMemberCall = ChatMember.normalizeCallSignToBaseCallSign(memberCall);
if (normalizedMemberCall != null && normalizedMemberCall.equalsIgnoreCase(normalizedKeyPart)) {
matchingMembers.add(member);
}
}
return matchingMembers;
}
/**
* Updates locator and derived direction values in the active model. The UI list
* contains the same member object, but the user-list refresh is still triggered
* on the JavaFX thread so TableView/map derived displays can repaint safely.
*/
public boolean updateActiveChatMemberLocator(ChatMember lookupMember, String newLocator) {
ChatMember activeMember = findActiveChatMember(lookupMember);
if (activeMember == null) {
return false;
}
activeMember.setQra(newLocator);
activeMember.setQrb(new Location().getDistanceKmByTwoLocatorStrings(chatPreferences.getStn_loginLocatorMainCat(), newLocator));
activeMember.setQTFdirection(new Location(chatPreferences.getStn_loginLocatorMainCat()).getBearing(new Location(newLocator)));
fireUserListUpdate("Locator changed");
return true;
}
public boolean updateActiveChatMemberState(ChatMember lookupMember, int newState) {
ChatMember activeMember = findActiveChatMember(lookupMember);
if (activeMember == null) {
return false;
}
activeMember.setState(newState);
fireUserListUpdate("User state changed");
return true;
}
/**
* Applies a UM3-style user-info update only to an already active chat member.
*
* ON4KST may send UM3 messages for stations that are not logged into the
* current channel, for example when a user changes locator/profile data on the
* website. Such stations must not be added to the visible chat member list,
* because they cannot be addressed in the channel. Their full information will
* be delivered again with UA0/UA5/UA2 when they actually join.
*
* @return true if an active member was found and updated, false if the UM3
* information was intentionally ignored.
*/
public boolean updateActiveChatMemberInfoIfPresent(ChatMember updatedMember) {
String key = buildActiveChatMemberKey(updatedMember);
if (key == null) {
return false;
}
ChatMember activeMember = activeChatMembersByCallAndCategory.get(key);
if (activeMember == null) {
return false;
}
activeMember.setName(updatedMember.getName());
activeMember.setQra(updatedMember.getQra());
activeMember.setState(updatedMember.getState());
activeMember.setLastActivity(updatedMember.getLastActivity());
activeMember.setActivityTimeLastInEpoch(updatedMember.getActivityTimeLastInEpoch());
activeMember.setQrb(updatedMember.getQrb());
activeMember.setQTFdirection(updatedMember.getQTFdirection());
fireUserListUpdate("User info updated");
return true;
}
/**
* Backward-compatible wrapper for older call sites. Despite the historic name,
* this method no longer adds unknown UM3 users to the active member model.
*/
public boolean updateOrAddActiveChatMemberInfo(ChatMember updatedMember) {
return updateActiveChatMemberInfoIfPresent(updatedMember);
}
/**
* Stores a newly detected QRG/band on all active category variants of a station.
* The old StringProperty is still set for compatibility with existing TableView
* columns and DXCluster code.
*/
public void applyDetectedFrequencyToActiveMembers(ChatMember sender, Band detectedBand, double detectedFrequencyMhz) {
if (sender == null || detectedBand == null || detectedFrequencyMhz <= 0) {
return;
}
String displayFrequency = String.valueOf(detectedFrequencyMhz);
String rawCall = sender.getCallSignRaw() != null ? sender.getCallSignRaw() : sender.getCallSign();
for (ChatMember member : findActiveChatMembersByRawCall(rawCall)) {
if (member == null) {
continue;
}
member.addKnownFrequency(detectedBand, detectedFrequencyMhz);
member.setFrequency(new SimpleStringProperty(displayFrequency));
}
// Also cover dummy senders that are not in the active model, e.g. [n/a] senders.
sender.addKnownFrequency(detectedBand, detectedFrequencyMhz);
sender.setFrequency(new SimpleStringProperty(displayFrequency));
fireUserListUpdate("Frequency detected");
}
private int findChatMemberIndexInUiListByKey(String key) {
if (key == null) {
return -1;
}
for (int i = 0; i < lst_chatMemberList.size(); i++) {
if (key.equals(buildActiveChatMemberKey(lst_chatMemberList.get(i)))) {
return i;
}
}
return -1;
}
@@ -2011,7 +2288,7 @@ public class ChatController implements ThreadStatusCallback, PstRotatorEventList
socket.close(); socket.close();
chatController.setConnectedAndLoggedIn(false); chatController.setConnectedAndLoggedIn(false);
chatController.getLst_chatMemberList().clear(); chatController.clearActiveChatMembers();
System.out.println("[Chatcontroller, Warning: ] Socket closed or disconnected"); System.out.println("[Chatcontroller, Warning: ] Socket closed or disconnected");
File diff suppressed because it is too large Load Diff
+1 -19
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="About KST4Contest"> <meta name="twitter:title" content="About KST4Contest">
<meta name="twitter:description" content="About KST4Contest and its contest-oriented ON4KST workflow."> <meta name="twitter:description" content="About KST4Contest and its contest-oriented ON4KST workflow.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -87,22 +85,7 @@
The project is open source and focused on practical contest station workflows. The project is open source and focused on practical contest station workflows.
Features are designed around real operating pressure, not theoretical UI concepts. Features are designed around real operating pressure, not theoretical UI concepts.
</p> </p>
<h2>Community supported</h2>
<p>
KST4Contest is developed independently.
Community support helps finance hosting,
online services,
testing across multiple operating systems
and future contest-oriented features.
</p>
</article> </article>
</section> </section>
</main> </main>
@@ -126,7 +109,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="Contact | KST4Contest"> <meta name="twitter:title" content="Contact | KST4Contest">
<meta name="twitter:description" content="Contact options for KST4Contest."> <meta name="twitter:description" content="Contact options for KST4Contest.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -114,7 +112,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -32
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="Download KST4Contest"> <meta name="twitter:title" content="Download KST4Contest">
<meta name="twitter:description" content="Download KST4Contest releases for Windows, Linux and macOS."> <meta name="twitter:description" content="Download KST4Contest releases for Windows, Linux and macOS.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -227,34 +225,6 @@
<a class="button secondary" href="/faq/">FAQ</a> <a class="button secondary" href="/faq/">FAQ</a>
</div> </div>
</div> </div>
</section>
<section class="section">
<div class="card content-card">
<h2>Enjoying KST4Contest?</h2>
<p>
If KST4Contest makes contesting easier for you,
please consider supporting future development.
</p>
<div class="actions">
<a class="button"
href="/support/">
❤️ Support Development
</a>
</div>
</div>
</section> </section>
</main> </main>
@@ -278,7 +248,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="KST4Contest FAQ"> <meta name="twitter:title" content="KST4Contest FAQ">
<meta name="twitter:description" content="Frequently asked questions about KST4Contest, ON4KST contest operation, AirScout integration and VHF/UHF/SHF contest workflow."> <meta name="twitter:description" content="Frequently asked questions about KST4Contest, ON4KST contest operation, AirScout integration and VHF/UHF/SHF contest workflow.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -157,7 +155,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="AirScout Integration"> <meta name="twitter:title" content="AirScout Integration">
<meta name="twitter:description" content="AirScout information helps operators evaluate AP windows, aircraft scatter timing and candidate stations during VHF/UHF/SHF contests."> <meta name="twitter:description" content="AirScout information helps operators evaluate AP windows, aircraft scatter timing and candidate stations during VHF/UHF/SHF contests.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -212,7 +210,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="Dual Chat Categories"> <meta name="twitter:title" content="Dual Chat Categories">
<meta name="twitter:description" content="Dual category support gives contest operators better overview when working across multiple ON4KST chat rooms."> <meta name="twitter:description" content="Dual category support gives contest operators better overview when working across multiple ON4KST chat rooms.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -188,7 +186,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="DXCluster Server"> <meta name="twitter:title" content="DXCluster Server">
<meta name="twitter:description" content="KST4Contest includes DXCluster functionality to support situational awareness during contest operation."> <meta name="twitter:description" content="KST4Contest includes DXCluster functionality to support situational awareness during contest operation.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -187,7 +185,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="KST4Contest Features"> <meta name="twitter:title" content="KST4Contest Features">
<meta name="twitter:description" content="Contest-optimized ON4KST features for VHF, UHF and SHF operation."> <meta name="twitter:description" content="Contest-optimized ON4KST features for VHF, UHF and SHF operation.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -173,7 +171,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="Log Synchronization"> <meta name="twitter:title" content="Log Synchronization">
<meta name="twitter:description" content="Log synchronization helps KST4Contest understand worked stations, active bands and contest context."> <meta name="twitter:description" content="Log synchronization helps KST4Contest understand worked stations, active bands and contest context.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -188,7 +186,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="Macros and Variables"> <meta name="twitter:title" content="Macros and Variables">
<meta name="twitter:description" content="Macros reduce repetitive typing and help operators respond quickly during active ON4KST contest sessions."> <meta name="twitter:description" content="Macros reduce repetitive typing and help operators respond quickly during active ON4KST contest sessions.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -187,7 +185,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="Priority Score System"> <meta name="twitter:title" content="Priority Score System">
<meta name="twitter:description" content="KST4Contest ranks stations by activity, direction, sked context, band information, QRG hints and operator workflow signals."> <meta name="twitter:description" content="KST4Contest ranks stations by activity, direction, sked context, band information, QRG hints and operator workflow signals.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -216,7 +214,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="Sked Reminder"> <meta name="twitter:title" content="Sked Reminder">
<meta name="twitter:description" content="KST4Contest keeps skeds in focus while the operator handles chat traffic, logging, bands and aircraft scatter timing."> <meta name="twitter:description" content="KST4Contest keeps skeds in focus while the operator handles chat traffic, logging, bands and aircraft scatter timing.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -210,7 +208,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="Timeline View"> <meta name="twitter:title" content="Timeline View">
<meta name="twitter:description" content="The timeline view helps operators understand short propagation windows and candidate timing during active contest operation."> <meta name="twitter:description" content="The timeline view helps operators understand short propagation windows and candidate timing during active contest operation.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -188,7 +186,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -5
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="KST4Contest Contest-Optimized ON4KST Chat Client"> <meta name="twitter:title" content="KST4Contest Contest-Optimized ON4KST Chat Client">
<meta name="twitter:description" content="KST4Contest is a modern ON4KST chat client built for VHF, UHF and SHF contest operation."> <meta name="twitter:description" content="KST4Contest is a modern ON4KST chat client built for VHF, UHF and SHF contest operation.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -239,7 +237,6 @@
<div class="actions"> <div class="actions">
<a class="button" href="/download/">Download KST4Contest</a> <a class="button" href="/download/">Download KST4Contest</a>
<a class="button secondary" href="/features/">Explore features</a> <a class="button secondary" href="/features/">Explore features</a>
<a class="button ghost" href="/support/">❤️ Support Development</a>
</div> </div>
</div> </div>
</section> </section>
@@ -278,7 +275,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="Legal Notice | KST4Contest"> <meta name="twitter:title" content="Legal Notice | KST4Contest">
<meta name="twitter:description" content="Legal notice for the KST4Contest website."> <meta name="twitter:description" content="Legal notice for the KST4Contest website.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -124,7 +122,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -198,7 +196,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -200,7 +198,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -335,7 +333,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -162,7 +160,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -284,7 +282,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -191,7 +189,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="KST4Contest Deutsches Handbuch"> <meta name="twitter:title" content="KST4Contest Deutsches Handbuch">
<meta name="twitter:description" content="Deutsches Benutzerhandbuch für KST4Contest."> <meta name="twitter:description" content="Deutsches Benutzerhandbuch für KST4Contest.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -187,7 +185,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -306,7 +304,6 @@ flatpak install kst4contest de.x08.KST4Contest//nightly
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -242,7 +240,6 @@ Zudem wird die <strong>primäre Chat-Kategorie</strong> (z. B. IARU Region 1 VHF
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -220,7 +218,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -231,7 +229,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -198,7 +196,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -335,7 +333,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -242,7 +240,6 @@ Also, select the <strong>primary chat category</strong> (e.g., IARU Region 1 VHF
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -162,7 +160,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -283,7 +281,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -191,7 +189,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="KST4Contest English Manual"> <meta name="twitter:title" content="KST4Contest English Manual">
<meta name="twitter:description" content="English user manual for KST4Contest."> <meta name="twitter:description" content="English user manual for KST4Contest.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -187,7 +185,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -306,7 +304,6 @@ flatpak install kst4contest de.x08.KST4Contest//nightly
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -220,7 +218,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -231,7 +229,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual"> <meta name="twitter:title" content="{{ manual.title }} | KST4Contest Manual">
<meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}"> <meta name="twitter:description" content="KST4Contest manual page: {{ manual.title }}">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -200,7 +198,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="KST4Contest Manual"> <meta name="twitter:title" content="KST4Contest Manual">
<meta name="twitter:description" content="Online manual for KST4Contest, the contest-optimized ON4KST chat client."> <meta name="twitter:description" content="Online manual for KST4Contest, the contest-optimized ON4KST chat client.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -99,7 +97,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="Hotfix Version 1.41.1"> <meta name="twitter:title" content="Hotfix Version 1.41.1">
<meta name="twitter:description" content="Contest-optimized ON4KST chat client for VHF, UHF and SHF ham radio contests."> <meta name="twitter:description" content="Contest-optimized ON4KST chat client for VHF, UHF and SHF ham radio contests.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -100,7 +98,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="KST4Contest Website Launch"> <meta name="twitter:title" content="KST4Contest Website Launch">
<meta name="twitter:description" content="Contest-optimized ON4KST chat client for VHF, UHF and SHF ham radio contests."> <meta name="twitter:description" content="Contest-optimized ON4KST chat client for VHF, UHF and SHF ham radio contests.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -104,7 +102,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="KST4Contest News"> <meta name="twitter:title" content="KST4Contest News">
<meta name="twitter:description" content="News, release updates and development notes for KST4Contest."> <meta name="twitter:description" content="News, release updates and development notes for KST4Contest.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -114,7 +112,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -20
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="Privacy Policy | KST4Contest"> <meta name="twitter:title" content="Privacy Policy | KST4Contest">
<meta name="twitter:description" content="Privacy policy for the KST4Contest website."> <meta name="twitter:description" content="Privacy policy for the KST4Contest website.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -105,22 +103,6 @@
GitHub is responsible for its own data processing. GitHub is responsible for its own data processing.
</p> </p>
<h2>
Support via Ko-fi
</h2>
<p>
This website links to Ko-fi for voluntary financial support.
Payments are processed entirely by Ko-fi and its payment providers.
KST4Contest itself never receives or processes payment information.
</p>
<h2>Your rights</h2> <h2>Your rights</h2>
<p> <p>
You may have the right to access, rectification, erasure, restriction of processing, You may have the right to access, rectification, erasure, restriction of processing,
@@ -155,7 +137,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -46
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="KST4Contest Roadmap"> <meta name="twitter:title" content="KST4Contest Roadmap">
<meta name="twitter:description" content="Planned enhancements for upcoming KST4Contest versions, generated from GitHub milestones and issues."> <meta name="twitter:description" content="Planned enhancements for upcoming KST4Contest versions, generated from GitHub milestones and issues.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -175,48 +173,6 @@
</section> </section>
<section class="section">
<div class="cta-panel">
<p class="eyebrow">
Community
</p>
<h2>
Development is driven by the community.
</h2>
<p>
Many roadmap items require
time,
online services
and infrastructure.
Community support helps accelerate development.
</p>
<div class="actions">
<a class="button"
href="/support/">
❤️ Support Development
</a>
</div>
</div>
</section>
</main> </main>
<footer class="site-footer"> <footer class="site-footer">
@@ -239,7 +195,6 @@ href="/support/">
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+1 -4
View File
@@ -17,7 +17,7 @@
<meta name="twitter:title" content="KST4Contest Screenshots"> <meta name="twitter:title" content="KST4Contest Screenshots">
<meta name="twitter:description" content="Screenshots of KST4Contest, the contest-optimized ON4KST chat client for VHF, UHF and SHF operators."> <meta name="twitter:description" content="Screenshots of KST4Contest, the contest-optimized ON4KST chat client for VHF, UHF and SHF operators.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235"> <link rel="stylesheet" href="/assets/css/main.css?v=1783632276803">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml"> <link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png"> <meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
@@ -57,8 +57,6 @@
<a href="/faq/">FAQ</a> <a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a> <a class="nav-cta" href="/download/">Download</a>
</nav> </nav>
</header> </header>
@@ -125,7 +123,6 @@
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p> <p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p> <p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p> <p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div> </div>
<div> <div>
+22 -26
View File
@@ -10,94 +10,90 @@
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/about/</loc> <loc>https://kst4contest.hamradioonline.de/about/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/contact/</loc> <loc>https://kst4contest.hamradioonline.de/contact/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/download/</loc> <loc>https://kst4contest.hamradioonline.de/download/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/faq/</loc> <loc>https://kst4contest.hamradioonline.de/faq/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/features/airscout/</loc> <loc>https://kst4contest.hamradioonline.de/features/airscout/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/features/dual-chat/</loc> <loc>https://kst4contest.hamradioonline.de/features/dual-chat/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/features/dx-cluster/</loc> <loc>https://kst4contest.hamradioonline.de/features/dx-cluster/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/features/</loc> <loc>https://kst4contest.hamradioonline.de/features/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/features/log-sync/</loc> <loc>https://kst4contest.hamradioonline.de/features/log-sync/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/features/macros/</loc> <loc>https://kst4contest.hamradioonline.de/features/macros/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/features/priority-score/</loc> <loc>https://kst4contest.hamradioonline.de/features/priority-score/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/features/sked-reminder/</loc> <loc>https://kst4contest.hamradioonline.de/features/sked-reminder/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/features/timeline/</loc> <loc>https://kst4contest.hamradioonline.de/features/timeline/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/</loc> <loc>https://kst4contest.hamradioonline.de/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/legal-notice/</loc> <loc>https://kst4contest.hamradioonline.de/legal-notice/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/manual/de/airscout-integration/</loc> <loc>https://kst4contest.hamradioonline.de/manual/de/airscout-integration/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/manual/de/</loc> <loc>https://kst4contest.hamradioonline.de/manual/de/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/manual/en/</loc> <loc>https://kst4contest.hamradioonline.de/manual/en/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/manual/</loc> <loc>https://kst4contest.hamradioonline.de/manual/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/privacy/</loc> <loc>https://kst4contest.hamradioonline.de/privacy/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/roadmap/</loc> <loc>https://kst4contest.hamradioonline.de/roadmap/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url> </url>
<url> <url>
<loc>https://kst4contest.hamradioonline.de/screenshots/</loc> <loc>https://kst4contest.hamradioonline.de/screenshots/</loc>
<lastmod>2026-07-11</lastmod> <lastmod>2026-07-09</lastmod>
</url>
<url>
<loc>https://kst4contest.hamradioonline.de/support/</loc>
<lastmod>2026-07-11</lastmod>
</url> </url>
</urlset> </urlset>
-271
View File
@@ -1,271 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Support KST4Contest</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Help support the ongoing development of KST4Contest.">
<link rel="canonical" href="https://kst4contest.hamradioonline.de/support/">
<meta property="og:type" content="website">
<meta property="og:title" content="Support KST4Contest">
<meta property="og:description" content="Help support the ongoing development of KST4Contest.">
<meta property="og:url" content="https://kst4contest.hamradioonline.de/support/">
<meta property="og:site_name" content="KST4Contest">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Support KST4Contest">
<meta name="twitter:description" content="Help support the ongoing development of KST4Contest.">
<link rel="stylesheet" href="/assets/css/main.css?v=1783804882235">
<link rel="alternate" type="application/rss+xml" title="KST4Contest News" href="/feed.xml">
<meta property="og:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta name="twitter:image" content="https://kst4contest.hamradioonline.de/assets/social/kst4contest-og.png">
</head>
<body>
<div class="site-bg"></div>
<header class="site-header">
<a class="brand" href="/">
<span class="brand-mark"></span>
<span>
<strong>KST4Contest</strong>
<small>ON4KST Contest Client</small>
</span>
</a>
<nav class="site-nav">
<a href="/">Home</a>
<a href="/features/">Features</a>
<a href="/screenshots/">Screenshots</a>
<a href="/manual/">Manual</a>
<a href="/news/">News</a>
<a href="/roadmap/">Roadmap</a>
<a href="/about/">About</a>
<a href="/faq/">FAQ</a>
<a href="/support/">Support</a>
<a class="nav-cta" href="/download/">Download</a>
</nav>
</header>
<main>
<section class="hero">
<p class="badge">Support Development</p>
<h1>Help shape the future of KST4Contest.</h1>
<p class="lead">
KST4Contest is an independent open-source project developed in my free time.
Your support helps cover infrastructure costs and enables new contest-oriented
features that would otherwise be difficult to implement.
</p>
<div class="actions">
<a class="button"
href="https://ko-fi.com/praktimarc"
target="_blank"
rel="noopener">
❤️ Support on Ko-fi
</a>
<a class="button secondary"
href="https://github.com/praktimarc/kst4contest">
View Source Code
</a>
</div>
</section>
<section class="section">
<div class="section-heading">
<p class="eyebrow">What previous support has enabled</p>
<h2>Real improvements funded by the community.</h2>
</div>
<div class="grid">
<article class="card">
<h3>🛰 PSTRotator Integration</h3>
<p>
Support helped justify the investment into rotor hardware and the required
PSTRotator license. Today KST4Contest can automatically control antennas
during contest operation.
</p>
</article>
<article class="card">
<h3>🗺 Interactive Map</h3>
<p>
The interactive station map required commercial map API usage.
Community support helped cover those API costs and made the feature possible.
</p>
</article>
</div>
</section>
<section class="section">
<div class="section-heading">
<p class="eyebrow">Future goals</p>
<h2>What future support will fund</h2>
</div>
<div class="grid">
<article class="card">
<h3>☁ Map API</h3>
<p>
Keeping the online map service available requires ongoing API usage.
</p>
</article>
<article class="card">
<h3>📡 Tropo Server</h3>
<p>
Future propagation prediction services and server-side calculations
will require dedicated hosting infrastructure.
</p>
</article>
<article class="card">
<h3>🔌 New Integrations</h3>
<p>
Development of additional interfaces, logging software integrations,
contest tools and automation features.
</p>
</article>
<article class="card">
<h3>🚀 Faster Development</h3>
<p>
More development time means more contest-oriented features,
better testing and better documentation.
</p>
</article>
</div>
</section>
<section class="section">
<div class="cta-panel">
<p class="eyebrow">Every contribution helps</p>
<h2>Whether it's one coffee or ongoing support.</h2>
<p>
Your support directly improves KST4Contest.
Unlike commercial software, every new feature is driven by real contest
experience and community feedback.
</p>
<div class="actions">
<a class="button"
href="https://ko-fi.com/praktimarc"
target="_blank"
rel="noopener">
❤️ Support KST4Contest
</a>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="footer-grid">
<div>
<strong>KST4Contest</strong>
<p>Contest-optimized ON4KST workflow for VHF/UHF/SHF operators.</p>
</div>
<div>
<strong>Project</strong>
<p><a href="/features/">Features</a></p>
<p><a href="/download/">Download</a></p>
<p><a href="/manual/">Manual</a></p>
<p><a href="/roadmap/">Roadmap</a></p>
</div>
<div>
<strong>Community</strong>
<p><a href="https://github.com/praktimarc/kst4contest">GitHub Repository</a></p>
<p><a href="https://github.com/praktimarc/kst4contest/issues">Issues</a></p>
<p><a href="/contact/">Contact</a></p>
<p><a href="/support/">❤️ Support KST4Contest</a></p>
</div>
<div>
<strong>Legal</strong>
<p><a href="/legal-notice/">Legal Notice</a></p>
<p><a href="/privacy/">Privacy Policy</a></p>
</div>
</div>
</footer>
</body>
</html>
+5
View File
@@ -81,5 +81,10 @@
</div> </div>
</div> </div>
</footer> </footer>
{% if heroFx %}
<script src="/assets/js/hero-radio-fx.js?v={{ build.version }}" defer></script>
{% endif %}
</body> </body>
</html> </html>
+198 -8
View File
@@ -7,10 +7,22 @@
--text: #f8fafc; --text: #f8fafc;
--muted: #aab6ca; --muted: #aab6ca;
--soft: #64748b; --soft: #64748b;
--accent: #38bdf8; /*--accent: #38bdf8;*/
--accent2: #a855f7; /*--accent2: #a855f7;*/
--accent3: #22c55e; /*--accent3: #22c55e;*/
--shadow: 0 24px 80px rgba(0, 0, 0, 0.45); --shadow: 0 24px 80px rgba(0, 0, 0, 0.45);
--accent: #54d63d;
--accent2: #8aef64;
--accent3: #31b54a;
--fx-plane: #79ff73;
--fx-plane-muted: rgba(121, 255, 115, 0.42);
--fx-beam: #52ff65;
--fx-beam-glow: rgba(82, 255, 101, 0.28);
--fx-reflection: #d2ffc8;
--fx-trail: rgba(121, 255, 115, 0.17);
--fx-grid: rgba(121, 255, 115, 0.055);
} }
* { * {
@@ -25,13 +37,39 @@ html {
body { body {
margin: 0; margin: 0;
min-height: 100vh; min-height: 100vh;
background: background:
radial-gradient(circle at 14% 12%, rgba(168, 85, 247, 0.30), transparent 28%), radial-gradient(
radial-gradient(circle at 82% 16%, rgba(56, 189, 248, 0.24), transparent 30%), circle at 14% 12%,
radial-gradient(circle at 50% 85%, rgba(34, 197, 94, 0.11), transparent 35%), rgba(52, 165, 57, 0.20),
linear-gradient(180deg, #050816 0%, #07111f 100%); transparent 30%
),
radial-gradient(
circle at 82% 16%,
rgba(95, 218, 82, 0.14),
transparent 32%
),
radial-gradient(
circle at 50% 85%,
rgba(32, 126, 55, 0.12),
transparent 36%
),
linear-gradient(
180deg,
#070b09 0%,
#0b1410 100%
);
color: var(--text); color: var(--text);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-family:
Inter,
ui-sans-serif,
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
sans-serif;
line-height: 1.65; line-height: 1.65;
overflow-x: hidden; overflow-x: hidden;
} }
@@ -561,4 +599,156 @@ a:hover {
.footer-grid { .footer-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
}
/*
* Interactive radio / aircraft-scatter hero layer
*/
.hero-radio-fx {
position: absolute;
inset: 0;
z-index: 0;
width: 100%;
height: 100%;
pointer-events: none;
opacity: 0.76;
}
.hero-fx-vignette {
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
background:
linear-gradient(
90deg,
rgba(7, 11, 9, 0.92) 0%,
rgba(7, 11, 9, 0.68) 34%,
rgba(7, 11, 9, 0.16) 64%,
rgba(7, 11, 9, 0.42) 100%
),
linear-gradient(
180deg,
rgba(7, 11, 9, 0.10),
rgba(7, 11, 9, 0.58)
);
}
.hero-split > *:not(.hero-radio-fx):not(.hero-fx-vignette) {
position: relative;
z-index: 2;
}
/*
* Operator-console badge styling
*/
.badge,
.eyebrow {
color: #caffc0;
background: rgba(52, 165, 57, 0.12);
border-color: rgba(105, 232, 88, 0.32);
box-shadow: 0 0 22px rgba(82, 255, 101, 0.08);
}
/*
* Light-mode identity:
* white/grey UI with red contest accents
*/
html[data-theme="light"] {
--bg: #f4f4f2;
--bg2: #ffffff;
--panel: rgba(255, 255, 255, 0.88);
--panel2: rgba(242, 242, 239, 0.92);
--border: rgba(91, 91, 86, 0.20);
--text: #252522;
--muted: #656560;
--soft: #888881;
--accent: #d33732;
--accent2: #f25a51;
--accent3: #ae2727;
--fx-plane: #df3732;
--fx-plane-muted: rgba(211, 55, 50, 0.38);
--fx-beam: #ef4039;
--fx-beam-glow: rgba(239, 64, 57, 0.22);
--fx-reflection: #ff8d84;
--fx-trail: rgba(211, 55, 50, 0.15);
--fx-grid: rgba(185, 47, 43, 0.045);
}
html[data-theme="light"] body {
background:
radial-gradient(
circle at 14% 12%,
rgba(211, 55, 50, 0.12),
transparent 30%
),
radial-gradient(
circle at 82% 16%,
rgba(242, 90, 81, 0.09),
transparent 32%
),
linear-gradient(
180deg,
#ffffff 0%,
#f0f0ed 100%
);
}
html[data-theme="light"] .hero-fx-vignette {
background:
linear-gradient(
90deg,
rgba(255, 255, 255, 0.95) 0%,
rgba(255, 255, 255, 0.76) 34%,
rgba(255, 255, 255, 0.18) 66%,
rgba(255, 255, 255, 0.48) 100%
),
linear-gradient(
180deg,
rgba(255, 255, 255, 0.06),
rgba(240, 240, 237, 0.62)
);
}
html[data-theme="light"] .badge,
html[data-theme="light"] .eyebrow {
color: #962a27;
background: rgba(211, 55, 50, 0.08);
border-color: rgba(211, 55, 50, 0.24);
}
@media (max-width: 900px) {
.hero-radio-fx {
opacity: 0.48;
}
.hero-fx-vignette {
background:
linear-gradient(
180deg,
rgba(7, 11, 9, 0.82),
rgba(7, 11, 9, 0.34)
);
}
html[data-theme="light"] .hero-fx-vignette {
background:
linear-gradient(
180deg,
rgba(255, 255, 255, 0.90),
rgba(255, 255, 255, 0.42)
);
}
}
@media (prefers-reduced-motion: reduce) {
.hero-radio-fx {
opacity: 0.28;
}
} }
+710
View File
@@ -0,0 +1,710 @@
(() => {
"use strict";
const canvas = document.querySelector("[data-hero-radio-fx]");
if (!(canvas instanceof HTMLCanvasElement)) {
return;
}
const context = canvas.getContext("2d", {
alpha: true,
desynchronized: true
});
if (!context) {
return;
}
const hero = canvas.closest(".hero");
if (!(hero instanceof HTMLElement)) {
return;
}
const reducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)"
);
const coarsePointer = window.matchMedia(
"(pointer: coarse)"
);
const state = {
width: 0,
height: 0,
dpr: 1,
running: false,
visible: true,
lastFrame: 0,
spawnAccumulator: 0,
beamAccumulator: 0,
animationFrameId: 0,
pointer: {
x: 0,
y: 0,
active: false
},
planes: [],
beams: [],
reflections: []
};
const clamp = (value, minimum, maximum) =>
Math.max(minimum, Math.min(maximum, value));
const randomBetween = (minimum, maximum) =>
minimum + Math.random() * (maximum - minimum);
function getCssColor(name, fallback) {
const value = getComputedStyle(document.documentElement)
.getPropertyValue(name)
.trim();
return value || fallback;
}
function getPalette() {
return {
plane: getCssColor("--fx-plane", "#79ff73"),
planeMuted: getCssColor("--fx-plane-muted", "rgba(121,255,115,.42)"),
beam: getCssColor("--fx-beam", "#52ff65"),
beamGlow: getCssColor("--fx-beam-glow", "rgba(82,255,101,.28)"),
reflection: getCssColor("--fx-reflection", "#c8ffbf"),
trail: getCssColor("--fx-trail", "rgba(121,255,115,.16)"),
grid: getCssColor("--fx-grid", "rgba(121,255,115,.055)")
};
}
function resizeCanvas() {
const bounds = hero.getBoundingClientRect();
state.width = Math.max(1, Math.round(bounds.width));
state.height = Math.max(1, Math.round(bounds.height));
state.dpr = Math.min(window.devicePixelRatio || 1, 1.5);
canvas.width = Math.round(state.width * state.dpr);
canvas.height = Math.round(state.height * state.dpr);
canvas.style.width = `${state.width}px`;
canvas.style.height = `${state.height}px`;
context.setTransform(
state.dpr,
0,
0,
state.dpr,
0,
0
);
if (!state.pointer.active) {
state.pointer.x = state.width * 0.54;
state.pointer.y = state.height * 0.42;
}
}
function getPlaneLimit() {
if (coarsePointer.matches) {
return 5;
}
if (state.width < 900) {
return 7;
}
return 12;
}
function createPlane() {
const margin = 55;
const edge = Math.floor(Math.random() * 4);
let x;
let y;
switch (edge) {
case 0:
x = randomBetween(0, state.width);
y = -margin;
break;
case 1:
x = state.width + margin;
y = randomBetween(0, state.height);
break;
case 2:
x = randomBetween(0, state.width);
y = state.height + margin;
break;
default:
x = -margin;
y = randomBetween(0, state.height);
break;
}
const targetX = state.pointer.active
? state.pointer.x
: state.width * randomBetween(0.38, 0.72);
const targetY = state.pointer.active
? state.pointer.y
: state.height * randomBetween(0.2, 0.72);
const angle = Math.atan2(targetY - y, targetX - x);
const speed = randomBetween(18, 34);
return {
x,
y,
previousX: x,
previousY: y,
velocityX: Math.cos(angle) * speed,
velocityY: Math.sin(angle) * speed,
angle,
size: randomBetween(5.5, 9),
age: 0,
maximumAge: randomBetween(12, 20),
steering: randomBetween(0.65, 1.2),
wobble: randomBetween(0, Math.PI * 2),
wobbleSpeed: randomBetween(0.7, 1.4),
hit: 0
};
}
function updatePlane(plane, deltaSeconds) {
plane.age += deltaSeconds;
plane.wobble += deltaSeconds * plane.wobbleSpeed;
plane.previousX = plane.x;
plane.previousY = plane.y;
const targetX = state.pointer.active
? state.pointer.x
: state.width * 0.56;
const targetY = state.pointer.active
? state.pointer.y
: state.height * 0.44;
const desiredAngle = Math.atan2(
targetY - plane.y,
targetX - plane.x
);
let angleDifference = desiredAngle - plane.angle;
while (angleDifference > Math.PI) {
angleDifference -= Math.PI * 2;
}
while (angleDifference < -Math.PI) {
angleDifference += Math.PI * 2;
}
plane.angle += angleDifference * plane.steering * deltaSeconds;
const speed = Math.hypot(
plane.velocityX,
plane.velocityY
);
const wobble = Math.sin(plane.wobble) * 0.14;
plane.velocityX =
Math.cos(plane.angle + wobble) * speed;
plane.velocityY =
Math.sin(plane.angle + wobble) * speed;
plane.x += plane.velocityX * deltaSeconds;
plane.y += plane.velocityY * deltaSeconds;
plane.hit = Math.max(0, plane.hit - deltaSeconds * 1.8);
}
function isPlaneExpired(plane) {
const margin = 180;
return (
plane.age > plane.maximumAge ||
plane.x < -margin ||
plane.x > state.width + margin ||
plane.y < -margin ||
plane.y > state.height + margin
);
}
function chooseBeamTarget() {
if (state.planes.length === 0) {
return null;
}
const targetX = state.pointer.active
? state.pointer.x
: state.width * 0.56;
const targetY = state.pointer.active
? state.pointer.y
: state.height * 0.44;
const candidates = state.planes
.map((plane) => ({
plane,
distance: Math.hypot(
plane.x - targetX,
plane.y - targetY
)
}))
.filter((entry) => entry.distance < 260)
.sort((a, b) => a.distance - b.distance);
if (candidates.length > 0) {
return candidates[0].plane;
}
return state.planes[
Math.floor(Math.random() * state.planes.length)
];
}
function createBeam(target) {
const leftStation = {
x: state.width * 0.12,
y: state.height * 0.82
};
const rightStation = {
x: state.width * 0.88,
y: state.height * 0.78
};
const source =
Math.abs(target.x - leftStation.x) <
Math.abs(target.x - rightStation.x)
? leftStation
: rightStation;
state.beams.push({
startX: source.x,
startY: source.y,
endX: target.x,
endY: target.y,
age: 0,
maximumAge: 0.62
});
target.hit = 1;
state.reflections.push({
x: target.x,
y: target.y,
age: 0,
maximumAge: 0.9,
radius: randomBetween(8, 15)
});
}
function updateEffects(deltaSeconds) {
for (const beam of state.beams) {
beam.age += deltaSeconds;
}
state.beams = state.beams.filter(
(beam) => beam.age < beam.maximumAge
);
for (const reflection of state.reflections) {
reflection.age += deltaSeconds;
reflection.radius += deltaSeconds * 30;
}
state.reflections = state.reflections.filter(
(reflection) =>
reflection.age < reflection.maximumAge
);
}
function drawGrid(palette) {
context.save();
context.strokeStyle = palette.grid;
context.lineWidth = 1;
const spacing = 54;
for (
let x = spacing;
x < state.width;
x += spacing
) {
context.beginPath();
context.moveTo(x, 0);
context.lineTo(x, state.height);
context.stroke();
}
for (
let y = spacing;
y < state.height;
y += spacing
) {
context.beginPath();
context.moveTo(0, y);
context.lineTo(state.width, y);
context.stroke();
}
context.restore();
}
function drawRadarTarget(palette) {
const x = state.pointer.active
? state.pointer.x
: state.width * 0.56;
const y = state.pointer.active
? state.pointer.y
: state.height * 0.44;
context.save();
context.strokeStyle = palette.planeMuted;
context.lineWidth = 1;
for (const radius of [24, 48, 82]) {
context.globalAlpha = 0.24 - radius / 500;
context.beginPath();
context.arc(x, y, radius, 0, Math.PI * 2);
context.stroke();
}
context.globalAlpha = 0.2;
context.beginPath();
context.moveTo(x - 100, y);
context.lineTo(x + 100, y);
context.moveTo(x, y - 100);
context.lineTo(x, y + 100);
context.stroke();
context.restore();
}
function drawPlane(plane, palette) {
context.save();
context.translate(plane.x, plane.y);
context.rotate(plane.angle);
const alpha = clamp(
Math.min(
plane.age * 1.8,
(plane.maximumAge - plane.age) * 1.8
),
0,
1
);
context.globalAlpha = alpha;
context.strokeStyle = palette.trail;
context.lineWidth = 1;
context.beginPath();
context.moveTo(-plane.size * 4.5, 0);
context.lineTo(-plane.size * 1.4, 0);
context.stroke();
context.fillStyle =
plane.hit > 0
? palette.reflection
: palette.plane;
context.shadowColor =
plane.hit > 0
? palette.reflection
: palette.beamGlow;
context.shadowBlur =
plane.hit > 0
? 16
: 5;
const size = plane.size;
context.beginPath();
context.moveTo(size * 1.65, 0);
context.lineTo(-size * 0.7, size * 0.34);
context.lineTo(-size * 0.15, size * 1.05);
context.lineTo(-size * 0.65, size * 1.08);
context.lineTo(-size * 1.35, size * 0.3);
context.lineTo(-size * 1.7, size * 0.28);
context.lineTo(-size * 1.12, 0);
context.lineTo(-size * 1.7, -size * 0.28);
context.lineTo(-size * 1.35, -size * 0.3);
context.lineTo(-size * 0.65, -size * 1.08);
context.lineTo(-size * 0.15, -size * 1.05);
context.lineTo(-size * 0.7, -size * 0.34);
context.closePath();
context.fill();
context.restore();
}
function drawBeam(beam, palette) {
const progress = beam.age / beam.maximumAge;
const alpha = Math.sin(progress * Math.PI);
context.save();
context.globalAlpha = alpha;
context.strokeStyle = palette.beamGlow;
context.lineWidth = 6;
context.shadowColor = palette.beam;
context.shadowBlur = 15;
context.beginPath();
context.moveTo(beam.startX, beam.startY);
context.lineTo(beam.endX, beam.endY);
context.stroke();
context.strokeStyle = palette.beam;
context.lineWidth = 1.4;
context.shadowBlur = 5;
context.beginPath();
context.moveTo(beam.startX, beam.startY);
context.lineTo(beam.endX, beam.endY);
context.stroke();
context.restore();
}
function drawReflection(reflection, palette) {
const progress =
reflection.age / reflection.maximumAge;
context.save();
context.globalAlpha = 1 - progress;
context.strokeStyle = palette.reflection;
context.lineWidth = 1.5;
context.shadowColor = palette.beam;
context.shadowBlur = 12;
context.beginPath();
context.arc(
reflection.x,
reflection.y,
reflection.radius,
0,
Math.PI * 2
);
context.stroke();
context.restore();
}
function drawStaticScene() {
const palette = getPalette();
context.clearRect(
0,
0,
state.width,
state.height
);
drawGrid(palette);
drawRadarTarget(palette);
}
function drawScene() {
const palette = getPalette();
context.clearRect(
0,
0,
state.width,
state.height
);
drawGrid(palette);
drawRadarTarget(palette);
for (const plane of state.planes) {
drawPlane(plane, palette);
}
for (const beam of state.beams) {
drawBeam(beam, palette);
}
for (const reflection of state.reflections) {
drawReflection(reflection, palette);
}
}
function animate(timestamp) {
if (!state.running) {
return;
}
const deltaSeconds = clamp(
(timestamp - state.lastFrame) / 1000 || 0,
0,
0.05
);
state.lastFrame = timestamp;
if (!state.visible || document.hidden) {
state.animationFrameId =
requestAnimationFrame(animate);
return;
}
state.spawnAccumulator += deltaSeconds;
state.beamAccumulator += deltaSeconds;
const planeLimit = getPlaneLimit();
const spawnInterval =
coarsePointer.matches ? 2.2 : 1.15;
if (
state.spawnAccumulator >= spawnInterval &&
state.planes.length < planeLimit
) {
state.spawnAccumulator = 0;
state.planes.push(createPlane());
}
const beamInterval =
coarsePointer.matches ? 3.3 : 1.8;
if (
state.beamAccumulator >= beamInterval &&
state.planes.length > 0
) {
state.beamAccumulator = 0;
const target = chooseBeamTarget();
if (target) {
createBeam(target);
}
}
for (const plane of state.planes) {
updatePlane(plane, deltaSeconds);
}
state.planes = state.planes.filter(
(plane) => !isPlaneExpired(plane)
);
updateEffects(deltaSeconds);
drawScene();
state.animationFrameId =
requestAnimationFrame(animate);
}
function start() {
cancelAnimationFrame(state.animationFrameId);
resizeCanvas();
if (reducedMotion.matches) {
state.running = false;
drawStaticScene();
return;
}
state.running = true;
state.lastFrame = performance.now();
if (state.planes.length === 0) {
const initialPlanes =
coarsePointer.matches ? 2 : 5;
for (let index = 0; index < initialPlanes; index++) {
const plane = createPlane();
plane.age = Math.random() * 2;
state.planes.push(plane);
}
}
state.animationFrameId =
requestAnimationFrame(animate);
}
function updatePointer(event) {
const bounds = hero.getBoundingClientRect();
state.pointer.x = clamp(
event.clientX - bounds.left,
0,
bounds.width
);
state.pointer.y = clamp(
event.clientY - bounds.top,
0,
bounds.height
);
state.pointer.active = true;
}
hero.addEventListener("pointermove", updatePointer, {
passive: true
});
hero.addEventListener("pointerleave", () => {
state.pointer.active = false;
});
window.addEventListener("resize", resizeCanvas, {
passive: true
});
document.addEventListener("visibilitychange", () => {
state.lastFrame = performance.now();
});
reducedMotion.addEventListener("change", start);
const resizeObserver = new ResizeObserver(() => {
resizeCanvas();
if (reducedMotion.matches) {
drawStaticScene();
}
});
resizeObserver.observe(hero);
const intersectionObserver = new IntersectionObserver(
(entries) => {
state.visible =
entries[0]?.isIntersecting ?? true;
state.lastFrame = performance.now();
},
{
threshold: 0.05
}
);
intersectionObserver.observe(hero);
start();
})();
+9
View File
@@ -2,9 +2,18 @@
layout: base.njk layout: base.njk
title: KST4Contest Contest-Optimized ON4KST Chat Client title: KST4Contest Contest-Optimized ON4KST Chat Client
description: KST4Contest is a modern ON4KST chat client built for VHF, UHF and SHF contest operation. description: KST4Contest is a modern ON4KST chat client built for VHF, UHF and SHF contest operation.
heroFx: true
--- ---
<section class="hero hero-split"> <section class="hero hero-split">
<canvas
class="hero-radio-fx"
data-hero-radio-fx
aria-hidden="true">
</canvas>
<div class="hero-fx-vignette" aria-hidden="true"></div>
<div> <div>
<p class="badge">The contest-optimized ON4KST client</p> <p class="badge">The contest-optimized ON4KST client</p>
<h1>KST4Contest</h1> <h1>KST4Contest</h1>