Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,25 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/).

---

## [Unreleased]

---

## [1.6.2.4] - 2026-08-22

### 🐛 Behoben

#### 🔢 Kanal-Anzeige: Backlog zeigte „Kanal N" statt Kanalname
- Nachrichten aus dem Backlog (DB) zeigten teils „Kanal 0"/„Kanal 1" statt „Mesh Hessen" o.ä., während Live-Nachrichten den Namen zeigten. Ursache: der Backlog nutzt den **gespeicherten** Kanalnamen — der stand als „Kanal N" fest, wenn die Nachricht empfangen/gespeichert wurde, **bevor** die Kanäle vom Gerät kamen. Jetzt wird `ChannelName` einheitlich aus dem **aktuellen Kanal-Index** aufgelöst (observable) und beim Eintreffen von Kanälen sowie nach dem Backlog-Laden neu berechnet.

#### ↩️ Reply wechselt jetzt auf den Kanal der Nachricht
- „Antworten" auf eine Kanalnachricht schaltete den aktiven Kanal nicht mehr um — die Antwort ging auf dem gerade gewählten Kanal raus. Jetzt wird beim Reply der aktive Kanal auf den umgestellt, auf dem die Nachricht empfangen wurde (Live **und** Backlog, da Live-Nachrichten jetzt auch den `ChannelIndex` tragen).

#### 🖱️ Debug-Log Auto-Scroll sprang nach oben
- Bei aktivem Auto-Scroll sprang das Debug-Log nach oben statt dem Ende zu folgen: Ab 10000 Zeilen wurde `DebugLogTextBox.Text` neu gesetzt (Trim), was die Scroll-Position auf den Anfang zurücksetzt — und das passierte **nach** `ScrollToEnd()`. Bei viel Traffic (ständiges Trimmen) wurde die Ansicht so bei jeder Zeile nach oben gerissen. Jetzt wird erst getrimmt, dann als letzte Aktion ans Ende gescrollt.

---

## [1.6.2.3] - 2026-08-16

### ✨ Hinzugefügt / 🔧 Geändert
Expand Down
4 changes: 3 additions & 1 deletion MeshhessenClient/MainWindow.Kiosk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,8 @@ private void LoadMessagesFromDbAsync()
_dbOldestTimestamp = entry.Timestamp;
}

// Rebuild visible list and scroll to newest
// Resolve stored "Kanal N" names against the current channel list, then rebuild.
RefreshChannelNames();
RebuildVisibleMessages();
if (_messages.Count > 0)
MessageListView.ScrollIntoView(_messages[^1]);
Expand Down Expand Up @@ -560,6 +561,7 @@ private void LazyLoadOlderMessages()
_dbOldestTimestamp = entry.Timestamp;
}

RefreshChannelNames();
RebuildVisibleMessages();

// Restore scroll position to the item that was previously first
Expand Down
45 changes: 39 additions & 6 deletions MeshhessenClient/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -589,14 +589,16 @@ private void OnLogMessageReceived(object? sender, string logMessage)
// on, so the user can scroll up and read without being yanked back down.
if (AutoScrollLogCheckBox?.IsChecked != false)
{
DebugLogTextBox.ScrollToEnd();

// Begrenze auf maximal 10000 Zeilen
// Trim FIRST: reassigning .Text resets the scroll position to the top, so it
// must happen before ScrollToEnd — otherwise every append past the limit yanks
// the view up instead of following the tail.
var lines = DebugLogTextBox.Text.Split('\n');
if (lines.Length > 10000)
{
DebugLogTextBox.Text = string.Join('\n', lines.Skip(lines.Length - 10000));
}

DebugLogTextBox.ScrollToEnd();
}
});
}
Expand Down Expand Up @@ -1839,11 +1841,12 @@ private void OnMessageReceived(object? sender, MessageItem message)
return; // Nicht anzeigen
}

// Setze ChannelName basierend auf Channel Index
// Setze ChannelName + ChannelIndex basierend auf Channel Index (einheitlich
// aufgelöst wie beim Backlog, damit die Anzeige konsistent ist).
if (uint.TryParse(message.Channel, out uint channelIndex))
{
var channel = _channels.FirstOrDefault(c => c.Index == channelIndex);
message.ChannelName = channel?.Name ?? $"Kanal {channelIndex}";
message.ChannelIndex = channelIndex;
message.ChannelName = ResolveChannelName(channelIndex);
}
else
{
Expand Down Expand Up @@ -2046,6 +2049,9 @@ private void OnChannelInfoReceived(object? sender, ChannelInfo channel)
}
_channels.Insert(insertAt, channel);

// A newly-arrived channel lets messages shown as "Kanal N" resolve to the real name.
RefreshChannelNames();

// Aktiviere Kanal-Auswahl wenn Kanäle vorhanden sind
if (_channels.Count > 0 && !ActiveChannelComboBox.IsEnabled)
{
Expand Down Expand Up @@ -4556,10 +4562,37 @@ private void DmMessageContextMenu_React_Click(object sender, RoutedEventArgs e)
// Forwarded from DirectMessagesWindow - handled there via EmojiPickerRequested
}

/// <summary>Resolve a channel index to its display name from the current channel list,
/// falling back to "Kanal N" when the channel isn't (yet) known.</summary>
private string ResolveChannelName(uint index)
{
var ch = _channels.FirstOrDefault(c => c.Index == index);
return !string.IsNullOrEmpty(ch?.Name) ? ch!.Name : $"Kanal {index}";
}

/// <summary>Re-resolve channel display names for all messages from the current channel
/// list. Backlog (DB) messages carry the name stored at receive time — often "Kanal N"
/// if the channels hadn't arrived yet — and channels can arrive after messages are shown.</summary>
private void RefreshChannelNames()
{
foreach (var m in _allMessages)
if (uint.TryParse(m.Channel, out var idx))
m.ChannelName = ResolveChannelName(idx);
}

private void MessageContextMenu_Reply_Click(object sender, RoutedEventArgs e)
{
if (MessageListView.SelectedItem is not MessageItem msg) return;
_replyToMessage = msg;

// Switch the active channel to the one the message arrived on, so the reply goes out
// on the right channel (channel messages carry an index 0-7 in msg.Channel).
if (uint.TryParse(msg.Channel, out var replyChanIdx))
{
var ch = _channels.FirstOrDefault(c => c.Index == replyChanIdx);
if (ch != null) ActiveChannelComboBox.SelectedItem = ch;
}

var preview = msg.Message?.Length > 60 ? msg.Message[..60] + "…" : msg.Message ?? string.Empty;
ReplyIndicatorText.Text = string.Format(Loc("StrReplyingTo"), msg.From, preview);
ReplyIndicatorPanel.Visibility = Visibility.Visible;
Expand Down
6 changes: 3 additions & 3 deletions MeshhessenClient/MeshhessenClient.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
<PublishDir>..\public\</PublishDir>

<AssemblyName>MeshhessenClient</AssemblyName>
<Version>1.6.2.3</Version>
<AssemblyVersion>1.6.2.3</AssemblyVersion>
<FileVersion>1.6.2.3</FileVersion>
<Version>1.6.2.4</Version>
<AssemblyVersion>1.6.2.4</AssemblyVersion>
<FileVersion>1.6.2.4</FileVersion>
<Company>Meshtastic Community</Company>
<Product>Meshhessen Client</Product>
<Description>Offline-fähiger Windows Client für Meshtastic Geräte</Description>
Expand Down
10 changes: 9 additions & 1 deletion MeshhessenClient/Models/MessageItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,15 @@ public string Message

public string Channel { get; set; } = string.Empty; // Channel Index (legacy, display string)
public uint ChannelIndex { get; set; } // raw channel index the packet arrived on
public string ChannelName { get; set; } = string.Empty; // Channel Name for display

// Observable: resolved from the current channel list, so a message shown before the
// channels arrived (e.g. DB backlog on connect) updates from "Kanal N" to the real name.
private string _channelName = string.Empty;
public string ChannelName
{
get => _channelName;
set { _channelName = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ChannelName))); }
}
public uint FromId { get; set; }
public uint ToId { get; set; }
public uint Id { get; set; } // Packet ID (for reactions)
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,16 @@ dotnet publish MeshhessenClient/MeshhessenClient.csproj -c Release -r win-x64 --

EXE liegt danach unter `public\MeshhessenClient.exe`. Alternativ: `build.bat` ausführen.

### Forks & Weiterverwendung

Der Quellcode darf im Rahmen der Lizenz geforkt und angepasst werden — wir freuen uns über abgeleitete Projekte.

> ⚠️ **Der Meshhessen-Tile-Server ist davon ausgenommen.** Die Karten-Infrastruktur (`tile.meshhessenclient.de` und die zugehörigen Vektor-/Raster-Endpunkte) wird **ausschließlich für den offiziellen Meshhessen Client** bereitgestellt und aus Spenden der Community finanziert. Forks, abgeleitete oder umgebaute Clients (auch für andere Mesh-Protokolle wie MeshCore) dürfen diese Server **nicht** nutzen und müssen **eigene Tile-Infrastruktur betreiben**.
>
> Der Client bringt dafür alles mit: der Karten-Modus **„Online – eigener Tile-Server"** in den Einstellungen erlaubt beliebige eigene Tile-URLs. Ein einfacher Tile-Cache (z. B. eigener OSM/OpenTopo-Proxy) ist schnell und günstig aufgesetzt.
>
> Unautorisierte Zugriffe auf die Meshhessen-Server werden technisch unterbunden.


## 🙏 Credits

Expand Down Expand Up @@ -703,6 +713,16 @@ dotnet publish MeshhessenClient/MeshhessenClient.csproj -c Release -r win-x64 --

The EXE will be at `public\MeshhessenClient.exe`. Alternatively, run `build.bat`.

### Forks & Reuse

You are welcome to fork and adapt the source code within the terms of the license — we're happy to see derivative projects.

> ⚠️ **The Meshhessen tile server is not part of that.** The map infrastructure (`tile.meshhessenclient.de` and the associated vector/raster endpoints) is provided **exclusively for the official Meshhessen Client** and is funded by community donations. Forks, derivative or repurposed clients (including ports to other mesh protocols such as MeshCore) **may not** use these servers and must **run their own tile infrastructure**.
>
> The client ships with everything you need for that: the **"Online – custom tile server"** map mode in settings accepts any tile URLs you like. A simple tile cache (e.g. your own OSM/OpenTopo proxy) is quick and cheap to set up.
>
> Unauthorized access to the Meshhessen servers is blocked at the technical level.


## 🙏 Credits

Expand Down
Loading