Skip to content

Commit 62fc57f

Browse files
BornToBeRootclaude
andauthored
Feature: Improve IP / Port Scanner and various fixes / improvements (#3660)
* Refactor: IP Scanner / Port Scanner options with init properties (R1) Replace the positional constructors (14 / 6 parameters) of IPScannerOptions and PortScannerOptions with init-only properties set via object initializer. No behaviour change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Split the IP Scanner host scan into discover / enrich (R2) The per-host lambda in ScanAsync is split into DiscoverAsync (ping, port scan and NetBIOS concurrently -> reachable) and EnrichAsync (DNS PTR, MAC address and vendor -> result), with ResolveHostnameAsync / ResolveMACAddressAsync as own methods. Prepares progressive results (enrich as soon as a host answers). No behaviour change. Port Scanner: nothing to split (per port probe -> result, PTR already runs concurrently). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Trim the trailing dot of DNS hostnames once (C2) The PTR record is fully qualified (e.g. "server.example.com."). The IP Scanner only trimmed it for custom commands and "Add profile", so the grid, the export and other features (DNS Lookup, Remote Desktop, ...) got the hostname with the trailing dot. It is now trimmed once when the result is created, in the IP Scanner and the Port Scanner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: IP Scanner export DNSHostname to CSV and XML (C4) The DNS hostname (without the NetBIOS fallback of Hostname) was only exported to JSON. Add it to CSV and XML, right after Hostname (JSON moved to the same position). Port Scanner: no separate DNS hostname, nothing to change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner / Port Scanner columns follow the options of the shown scan (C5) Columns, Copy menu entries and the row details (IP Scanner) were bound to the current settings. After changing a setting the shown results got columns without data (e.g. MAC address enabled after the scan) or lost columns with data, the row details showed "Port scan is disabled" for scanned ports. The view models keep the options of the shown scan (ScanOptions, until the first scan the settings when the tab was opened) and the views bind to them via the BindingProxy (DataGrid columns are not in the visual tree). Port Scanner: Hostname column and Copy > Hostname. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Docs: Changelog with separate entries per tool (IP Scanner, Port Scanner) Split the combined "IP Scanner / Port Scanner" section and the "also in the ..." / "the same applies to ..." remarks into separate entries in the section of each tool (IP Scanner, Port Scanner, Ping Monitor, Traceroute). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Trim the trailing dot of PTR hostnames in all tools (review #3) The trailing dot was only trimmed in the IP Scanner and Port Scanner (C2). Ping Monitor, Traceroute, Connections and the Network Connection widget still showed "server.example.com.". Trim it once in DNSClient.ResolvePtrAsync instead and remove the per-tool trims (IP Scanner, Port Scanner, Ping Monitor title / notification). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Keep the trailing dot of single-label PTR names (review #1) Trimming the trailing dot turned a single-label PTR name like "gateway." into "gateway". DNSClientHelper.IsBareHostname treats that as a bare hostname, so with "Add DNS suffix" enabled, DNS Lookup and every other tool that resolves it (e.g. when opened from the IP Scanner) queried "gateway.<suffix>" instead. New DNSClientHelper.TrimTrailingDot only trims fully qualified names with more than one label, used by DNSClient.ResolvePtrAsync. Unit tests added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Columns follow the settings while no results are shown (review #2) Since C5 the columns, Copy menu and row details were a snapshot of the settings when the tab was opened until the first scan - before, they followed the settings live. While no results are shown (and no scan is running), the view models now refresh ScanOptions when a setting that affects them changes (IP Scanner: resolve hostname, port scan, NetBIOS, MAC address; Port Scanner: resolve hostname). The snapshot no longer allocates the ICMP buffer (up to 65500 bytes per tab), it is only created for a scan. The settings handler is removed when the tab is closed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Required scan options (review #4) With init properties (R1) a forgotten option silently became 0 / false (e.g. MaxHostThreads = 0 -> Parallel.ForEachAsync throws), the positional constructor caught this at compile time. Mark all options of IPScannerOptions and PortScannerOptions as required. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner CSV export with DNSHostname as last column (review #5) C4 inserted the DNSHostname column after Hostname, which moved every following column by one - scripts / spreadsheets that read the CSV by position would read the wrong values. Add it as last column instead (XML and JSON are read by name and keep it after Hostname). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Compute HostDiscovery.IsReachable once (review #6) IsReachable was a computed property that went through the port list again on every read (about 4 times per host). Compute it once in the initializer like IsAnyPortOpen. No behaviour change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: IP Scanner resolves DNS hostname and MAC address concurrently (review #7) EnrichAsync ran the PTR lookup and the MAC address lookup (neighbor table, local interfaces, OUI) one after the other, although they don't depend on each other. Start both and await them together - a host is finished after the slower one instead of after both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Trim single-label hostnames for display and custom commands (review A) Since the PTR name keeps the dot of a single-label name (review #1, so other tools don't append the DNS suffix), the removed per-tool trims (C2, review #3) made custom commands ({{Hostname}}), the profile name of "Add profile" and the Ping Monitor title / notification use "gateway." instead of "gateway" (main). For custom commands this is more than cosmetic: Windows resolves a name with a trailing dot via DNS only (no suffix search, no LLMNR / NetBIOS). Trim the dot again in these places (display / external programs); the value passed to other NETworkManager tools keeps it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: IP Scanner result options for the shown columns (review D) ScanOptions kept the full IPScannerOptions of the last scan for the lifetime of the tab (ports array, ICMP buffer - up to ~320 KB for 1-65535 / 65500 bytes) only to read four flags, and CreateScanOptions(null) created options for display that claimed a port scan without ports. New IPScannerResultOptions record with the four flags the view binds to (hostname, port scan, NetBIOS, MAC address), created from the options of the scan or from the settings. CreateScanOptions is only used for a scan again. Port Scanner unchanged: PortScannerOptions only holds six scalar values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Columns follow the settings whenever the result is empty (review B) ScanOptions only followed the settings if a setting changed while no results were shown. An empty result could still keep the columns of the previous scan: Results.Clear() ran long before the options of the new scan were set, and a scan that was canceled during the preparation or found nothing didn't update them either. The handler also listed the relevant settings by hand (easy to miss when a column is added). UpdateScanOptionsIfNoResults is called after the results are cleared, when a scan is complete and on any settings change (IP Scanner + Port Scanner). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Shared PTR lookup for the IP Scanner and Port Scanner (review C) IPScanner and PortScanner had an identical private ResolveHostnameAsync (PTR lookup, stop waiting on cancellation, empty string on error). Replace both with DNSClient.ResolvePtrOrEmptyAsync. No behaviour change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: HostDiscovery as class instead of record (review E) HostDiscovery caches IsAnyPortOpen / IsReachable at construction. As a positional record, a "with" expression would copy these cached values instead of recomputing them, and the generated equality / ToString members are unused. Use a private sealed class with read-only properties. No behaviour change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Remove dead null checks of the NetBIOS info (review F) EnrichAsync checked discovery.NetBIOSInfo with ?. three times, but it is never null (an empty NetBIOSInfo if NetBIOS is disabled or there was no answer) and HostDiscovery.IsReachable already dereferences it. Keep the ?? fallbacks for its values, which may be null. No behaviour change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner XML export with DNSHostname as last element (review G) The CSV export got DNSHostname as last column (review #5), but the XML export still inserted it after Hostname (moving the following elements) and the JSON export moved the existing property next to Hostname. XML: last element, JSON: back to its previous position - no existing column / element / property changes its position compared to the last release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: IP Scanner shows hosts before DNS / MAC are resolved (phase 3, step 23) IPScannerHostInfo is observable (PropertyChangedBase, setters internal to the model). Hostname / MACAddress / Vendor are computed from their sources (DNS -> NetBIOS, neighbor table -> NetBIOS), so a value that arrives later updates them. The scanner raises HostFound right after the discovery (ping, port scan, NetBIOS) and the DNS hostname and MAC address / vendor as HostUpdated when they arrive (replaces HostScanned). The scanner never changes a host itself: the view model buffers HostFound and HostUpdated in one list (order kept, an update always after its host) and applies them on the UI thread in the existing 150 ms flush. A host is only finished (progress) when all its lookups are finished, and ScanComplete is raised only after every host body returned - the view model flushes the remaining updates before the scan ends. Events of a scanner other than the running one are ignored and logged (guard only, can't happen). Export copies the rows on the UI thread (Clone), since they may still change during a scan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: IP Scanner shows a host on its first answer (phase 3, step 24, P9) ScanHostAsync replaces DiscoverAsync / HostDiscovery: ping, port scan and NetBIOS run concurrently and are processed as they finish (Task.WhenAny). The port scan signals the first answering port (open or refused) right away. On the first answer the host is raised in HostFound - with placeholders for the probes that are still running (IsPingPending / IsPortScanPending / IsNetBIOSPending) - counted as up, and the DNS hostname and MAC address lookups start (P9). Every probe that finishes afterwards is raised as HostUpdated. Unreachable hosts are counted / raised (show unreachable) when all probes are finished. A host still only finishes (progress, and the scan with it) when every probe and lookup is finished: a finally waits for all of them, also when a probe was canceled or failed. View: Ping status, Port status and NetBIOS reachable show "..." while their probe is running (new PendingToStringConverter); the ping details and "No open ports found" are hidden until the ping / port scan is finished. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner / Port Scanner progress counters from the scanner state The view models stored the progress value of the ProgressChanged event args. Events of different hosts / ports raised at the same time can arrive in a different order, so an older (smaller) value could overwrite a newer one - also the last one, so the result could end with e.g. 255 of 256 hosts scanned. Step 24 raises ProgressChanged also when a host is found, which makes this more likely. The scanners expose HostsScanned / PortsScanned; the view models read all counters from the scanner on every event and once more in ScanComplete (final values). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: IP Scanner Time column (phase 3, step 26, M3) New Time column after Ping status with the round trip time of the ping reply ("-/-" without a reply, "..." while the ping is running - optional third value of PingTimeToStringConverter, existing users unchanged). Sorting by time uses a custom comparer: hosts without a ping reply (time 0) are always last, in both directions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner passes the DNS hostname, not a NetBIOS name, to other tools (phase 3, step 27, C1) The hostname shown in the result falls back to the NetBIOS computer name. That name was passed to other tools (Port Scanner, Remote Desktop, PuTTY, ...) and to DNS Lookup / RDAP > Hostname, where it may not be resolvable via DNS. Other tools now get the DNS hostname, otherwise the IP address; DNS lookup / RDAP > Hostname are only enabled with a DNS hostname. Custom commands keep {{Hostname}} = shown hostname (documented). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: IP Scanner / Port Scanner copy multiple results (phase 3, step 28, C3) The context menu for multiple selected results only had Export. New Copy submenu (same strings and style as the single-row Copy menu), one value per line in the order shown in the grid, without duplicates: - IP Scanner: IP address, Hostname, MAC address (visibility like the single-row menu / the columns of the shown scan) - Port Scanner: IP address, Hostname, Port, IP address:Port (IPEndPoint format, IPv6 in brackets) The grid order helper is shared with the export (GetSelectedResultsInGridOrder). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: IP Scanner / Port Scanner search (phase 3, step 29, M1) Search box right of the Result header (SearchTextBox, width 250, delayed filter like the Firewall - GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan): - IP Scanner: IP address, hostname, MAC address, vendor, shown ports (number or service). Live filtering on Hostname / MACAddress / Vendor / Ports, so a host found before its hostname or MAC address arrived is filtered again. - Port Scanner: IP address, hostname, port, protocol, service, description, status (also translated). Results don't change after they were added. Enter in the search box filters right away (ApplySearch) and is handled, so it doesn't trigger the scan button (IsDefault) and clear the results. Counters always count all hosts / ports; Export > all exports the filtered results (documented). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: IP Scanner Web Console / Wake on LAN, Port Scanner Web Console (phase 3, step 30, M2) IP Scanner context menu: - Web Console: opens the host (DNS hostname, otherwise the IP address - C1) via https if 443 is open, otherwise the first open of 80 / 8443 / 8080, http on the default port if none is open or the port scan is still running/disabled. - Wake on LAN: sends the magic packet to the MAC address (WakeOnLANRequest), only enabled with a MAC address. Port Scanner: Web Console for an open web port (80, 443, 8080, 8443). New WebConsoleUrlHelper (URL from host + ports: scheme, default ports left out, IPv6 in brackets with an encoded zone id) with unit tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: IP Scanner / Port Scanner rescan selected hosts (phase 3, step 31, M4) Context menu Rescan (single and multiple rows, disabled while a scan runs), new string "Rescan". IP Scanner: scans the IP addresses of the selected rows again with the options of the shown scan (kept as _lastScanOptions, so the columns stay valid) and new option ReportAllHosts (every host is raised, also if unreachable now, without showing all ports like ShowAllResults). The scanner creates new results; their values are copied into the existing rows (IPScannerHostInfo.CopyFrom, selection and expanded details stay) and their updates are applied to these rows (IPScannerHostUpdatedArgs.ApplyTo). Rows are matched by the new scanned IPAddress of IPScannerHostInfo (PingInfo has the reply address). Up / down are counted from the rows during the rescan, the progress of the last scan is restored afterwards. Port Scanner: the rows of the selected hosts are removed and scanned again with the ports and options of the last scan; open / closed = counters of the other hosts + the ones of the rescan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Chore: Translatable counter texts in IP Scanner, Port Scanner and Ping Monitor (phase 3, step 33, T1) The counters were built from the number and a separate word ("{0} {1}" with Strings.Up / Down / Open / Closed / Paused), so translations couldn't change the word order. New strings XUp / XDown / XOpen / XClosed / XPaused ("{0} Up" etc., same English text) used as StringFormat in the IP Scanner and Port Scanner and as format of PingMonitorGroupSummaryConverter (Ping Monitor group header). The old strings stay (used elsewhere). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: IP Scanner setting to expand the details of all hosts New setting "Expand host view" (IPScanner_ExpandHostView, default off, same string as the Ping Monitor setting): the result grid shows the row details of every host (RowDetailsVisibilityMode Visible instead of Collapsed), also of hosts added while the scan is running. It follows the current setting (a display preference, not an option of the scan); a row expanded / collapsed with its button keeps its state (local DetailsVisibility of the row). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner crash when the first host is found (typed binding paths) Starting a scan crashed with ArgumentNullException (Parameter 'key') in PropertyChangedEventManager.AddListener. Since step 23 IPScannerHostInfo implements INotifyPropertyChanged, so WPF registers a PropertyChanged listener for every binding path part on it - with the name the path parser stored (SourceValueInfo.propertyName). For a typed path part like "(network:IPScannerHostInfo.IsPingPending)" that name is only set if WPF can resolve the owner type early while parsing the path; in templates / child bindings of a MultiBinding it is resolved later, the stored name stays null (verified in the decompiled PropertyPath / PropertyPathWorker of WPF 10.0.13). Before, the source wasn't INotifyPropertyChanged and the name was never used. Use plain paths for bindings on IPScannerHostInfo (e.g. "IsPingPending", "PingInfo.Status") and for the view model via the BindingProxy ("Data.ScanOptions.ResolveHostname", "Data.SelectedResult.DNSHostname", "Data.IsRunning") in the IP Scanner and Port Scanner views. Typed paths on PortScannerPortInfo (not INotifyPropertyChanged) are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Docs: Conventions - plain binding paths on INotifyPropertyChanged items Add the rule behind the IP Scanner crash fix to the feature-review conventions: typed binding paths "(ns:Type.Property)" on items that raise PropertyChanged can crash (null property name for the PropertyChanged listener). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: SearchTextBoxHelper - Enter in a search box only filters New attached properties (NETworkManager.Utilities.WPF): HandleEnter handles Enter in a search box (PreviewKeyDown), so it doesn't trigger the default button of the view / dialog / window (e.g. start a scan, close a dialog with OK - IsDefault is window wide, so also the application search of the main window could trigger the default button of the shown tool). EnterCommand runs a command first, e.g. to apply a delayed search right away. Used per tool in the next commits; convention added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: IP Scanner search Enter via SearchTextBoxHelper Replace the PreviewKeyDown handler in the view (step 29) with SearchTextBoxHelper.EnterCommand bound to the new ApplySearchCommand (filter right away). Same behaviour: Enter filters and doesn't start a scan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Port Scanner search Enter via SearchTextBoxHelper Replace the PreviewKeyDown handler in the view (step 29) with SearchTextBoxHelper.EnterCommand bound to the new ApplySearchCommand (filter right away). Same behaviour: Enter filters and doesn't start a scan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: Firewall search Enter filters right away Enter in the search box applies the delayed search right away (SearchTextBoxHelper.EnterCommand -> ApplySearchCommand) and is handled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: Profiles search Enter filters right away Settings > Profiles: Enter in the search box applies the delayed search right away (SearchTextBoxHelper.EnterCommand -> ApplySearchCommand) and is handled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Profile import result search Enter only filters Enter in the search box of the import result dialog triggered the default button (import). It now applies the delayed search right away (SearchTextBoxHelper.EnterCommand -> ApplySearchCommand) and is handled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: Connections search with delay, Enter filters right away The search filtered on every key press (the connection list can be long and is refreshed automatically). Filter after the shared delay while typing (GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan, like the Firewall); Enter applies it right away (SearchTextBoxHelper.EnterCommand -> ApplySearchCommand) and is handled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: Listeners search with delay, Enter filters right away The search filtered on every key press. Filter after the shared delay while typing (GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan, like the Firewall); Enter applies it right away (SearchTextBoxHelper.EnterCommand -> ApplySearchCommand) and is handled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: Neighbor Table search with delay, Enter filters right away The search filtered on every key press. Filter after the shared delay while typing (GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan, like the Firewall); Enter applies it right away (SearchTextBoxHelper.EnterCommand -> ApplySearchCommand) and is handled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: Hosts File Editor search with delay, Enter filters right away The search filtered on every key press. Filter after the shared delay while typing (GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan, like the Firewall); Enter applies it right away (SearchTextBoxHelper.EnterCommand -> ApplySearchCommand) and is handled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: WiFi search with delay, Enter filters right away The search filtered on every key press. Filter after the shared delay while typing (GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan, like the Firewall); Enter applies it right away (SearchTextBoxHelper.EnterCommand -> ApplySearchCommand) and is handled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Application search Enter only filters A default button (IsDefault) is window wide: Enter in the application search of the main window could trigger the default button of the shown tool (e.g. start a scan). SearchTextBoxHelper.HandleEnter handles it, the list is filtered while typing as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Chore: Settings search Enter only filters SearchTextBoxHelper.HandleEnter on the settings search, so Enter never triggers a default button there (consistent with the other search boxes). No visible change, the list is filtered while typing as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Chore: Language search Enter only filters SearchTextBoxHelper.HandleEnter on the search of Settings > Language, so Enter never triggers a default button there (consistent with the other search boxes). No visible change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port profiles dialog search Enter only filters Enter in the search box triggered the default button (OK) and closed the dialog with the current selection. SearchTextBoxHelper.HandleEnter handles it, the list is filtered while typing as before. Enter in the list still selects (KeyBinding on the DataGrid). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: SNMP OID profiles dialog search Enter only filters Enter in the search box triggered the default button (OK) and closed the dialog with the current selection. SearchTextBoxHelper.HandleEnter handles it, the list is filtered while typing as before. Enter in the list still selects (KeyBinding on the DataGrid). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Chore: Port profiles dialog search box indentation Align the SearchTextBoxHelper attribute with the other attributes of the TextBox (formatting of deffdb78c). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner columns can be resized beyond the window (#3557, prototype) With a visible star column (Vendor, Width="*") the DataGrid only lets a column grow by taking the width from the columns to its right (down to their MinWidth, DataGridColumnCollection.RecomputeColumnWidthsOnColumnPositiveResize); the rest is dropped, so a column couldn't be made wider than the grid (also not with a double-click on the gripper) unless there was free space. New DataGridHelper.FillLastColumn: the last visible column fills the remaining width with a pixel width (max(base width, available width - other visible columns); base = MinWidth or the width the user resized it to), updated on size / viewport / column width / visibility changes, previous fill column restored when another one becomes the last. No star column -> columns grow freely, the grid scrolls horizontally. Used in the IP Scanner instead of Width="*" and RefreshStarColumnsOnFirstRow (not needed without a star column). Prototype - roll out to the other tools after testing on Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Columns can be resized beyond the window in all tools (#3557) Roll out DataGridHelper.FillRemainingWidth (prototype in the IP Scanner, renamed from FillLastColumn) to every DataGrid with a star column: the star width is removed, the grid fills the remaining width with a pixel width - the column marked with the new DataGridHelper.IsFillColumn (if the star column was not the last one, e.g. Traceroute hostname, Firewall, DNS Lookup, About) or the last visible column. Grids with several star columns (port / OID profiles): the widest one fills, the others get a MinWidth. Fill columns without MinWidth get MinWidth 150. RefreshStarColumnsOnFirstRow removed (no star columns left). Convention added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Update next-release.md * Fix: IP Scanner crash when sorted by MAC address (review 5, #1) Sorting by MAC address converted every MAC address to bytes, and an empty one (host behind a router, MAC address not resolved, or still being resolved while the scan runs) throws in MACAddressHelper.ConvertStringToByteArray - the sort failed with an unhandled exception and the application crashed. Since the results are progressive, a host added to a view sorted by MAC address before its MAC address arrived crashed it as well. Hosts without a MAC address are now compared before the bytes and are always at the end (like hosts without a ping reply in the Time column). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner / Port Scanner scan an IP address once, also with different hostnames (review 5, #2) The de-duplication of the input keeps every hostname of an IP address (needed by the Ping Monitor, which shows the hostname of the input). The scanners ignore the hostname of the input and resolve it via PTR (NetBIOS) - an IP address entered with two names (e.g. two names of one server, or a load balancer) was scanned twice with the same result: two identical rows in the IP Scanner, every port twice in the same group in the Port Scanner, and the counters doubled. Both scanners now scan every IP address once (IPv6 addresses with a different scope id stay different hosts). Ping Monitor unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port Scanner rescan removes the old rows at once (review 5, #3) Rescan removed the rows of the rescanned hosts one by one. Every removal searches the collection and the sorted, grouped view, so the time grows with rows x rows - with closed ports shown, a rescan of a host scanned with 1-65535 freezes the window for minutes. The remaining rows are now set with one reset (ObservableRangeCollection.ReplaceAll); the removed rows were the selection anyway. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner sort arrow of the previous column kept (review 5, #4) The columns with an own comparer (IP address, MAC address, Time) handle the Sorting event, so the built-in sort - which resets the sort direction of the other columns - doesn't run. After sorting by Hostname and then by IP address, both columns showed a sort arrow. Reset the other columns like the Port Scanner does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner NetBIOS details hidden while the request is running (review 5, #5) The row details showed the placeholder of a running NetBIOS request as result ("-/-" for computer name, domain and user), the values appeared later. The ping details and the ports are already hidden while their probe is running - the NetBIOS details now too (IsNetBIOSPending). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner Copy > Ports hidden with the port scan disabled (review 5, #6) Copy > Port status already follows the port scan option of the shown scan, Copy > Ports was always shown and copied an empty text without a port scan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner down counter negative after a rescan of a canceled scan (review 6, #1) During a rescan the down counter was the scanned hosts of the last scan minus the reachable rows. A host is counted as up when it is found, but as scanned only when DNS / MAC are finished - after a canceled scan there can be more reachable rows than scanned hosts (e.g. "-7 Down"), and the value stayed after the rescan. Use the hosts counted as up or down in the last scan instead, never below 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner / Port Scanner canceled rescan keeps the previous results (review 6, #2) IP Scanner: a rescanned host is copied into its rows as soon as it answers, with placeholders for the probes that are still running (no ports, empty hostname / MAC address, "..."). Canceled before they finished, the rows lost their complete results for good (also for Copy, Export, Web Console). The rows are copied before the rescan and restored if it is canceled or fails. Port Scanner: the rows of the rescanned hosts are removed when the rescan starts - canceled, only the ports scanned so far were left. They are restored as well, with the counters of the last scan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: One entry per IP address from HostRangeHelper for the scanners (review 6, #3) The de-duplication by IP address of review 5 #2 ran DistinctBy on the UI thread after every resolution - also for a single range that is already distinct (fast path of SortAndRemoveDuplicates). For a confirmed /8 that is 16.7 M hashed IP addresses, a second list and a large HashSet before the scan starts. ResolveAsync has a keepAllHostnames parameter instead: the merge of the entries of one IP address keeps the first hostname only (IP Scanner, Port Scanner) or all of them (Ping Monitor). The fast path stays free, the code duplicated in both view models is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port Scanner rescan keeps collapsed groups and the scroll position (review 6, #4) The single reset of review 5 #3 recreates every host group: their expanders are expanded by default, so groups the user collapsed opened again and the grid lost its scroll position. ReplaceResults removes / adds up to 1000 rows one by one (other groups unchanged) and only uses one reset for more rows, where single changes would freeze the window. Also used to restore the rows of a canceled rescan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner MAC address of IPv6 hosts with a scope id (review 6, #5) The neighbor table map was keyed without the IPv6 scope id, while the input keeps it: "fe80::1%3; fe80::1%7" (e.g. the routers of two interfaces) are two hosts, but both got the MAC address of the last "fe80::1" entry - also used for Wake on LAN and Add profile. The map now contains every IPv6 entry with its scope id (exact lookup for an address entered with one) and without it (an address entered without scope id, as before). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner sort by MAC address / Time in a fixed order for equal values (review 6, #6) Hosts with the same MAC address or ping time - especially all hosts without a MAC address or without a reply, and the many 0 ms replies on a LAN - compared as equal. The sort of the collection view is not stable, so these hosts were shown in a random order that changed with every sort. They are sorted by IP address now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner requests NetBIOS from IPv4 addresses only (review 6, #7) NetBIOS (UDP 137) doesn't exist over IPv6, but the request was sent to IPv6 hosts as well: every IPv6 host kept "..." in NetBIOS reachable and wasn't finished until the NetBIOS timeout (default 2 s), then showed "No". Skipped for IPv6 now (result: not reachable, right away). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner export of running probes as empty values (review 6, #8) Export during a scan (or of a scan canceled before every probe finished) wrote the placeholders of probes that were still running as results: a running port scan as PortStatus "Closed", a running ping as PingStatus "Unknown" with timestamp / TTL / bytes, a running NetBIOS request as not reachable. These values are empty now (CSV empty, XML empty element, JSON null); finished results are exported as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Chore: IP Scanner help icon for NetBIOS IPv4 only (review 6, #7) Scan for NetBIOS has a help icon with the tooltip that only IPv4 addresses are scanned for NetBIOS (NetBIOS doesn't exist over IPv6), like the other settings with a help icon (e.g. Firewall > Show all rules). Docs as note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner scan aborted by a link-layer address that is not a MAC address (final review, #2) The neighbor table and the local network interfaces can have link-layer addresses of another length than 6 bytes (e.g. 4 bytes = the IPv4 address of an ISATAP tunnel, 8 bytes on Teredo). MACAddressHelper.GetDefaultFormat always reads 6 hex pairs: a shorter address threw in the MAC lookup of a host, which aborted the whole scan (exception as status message); a longer one was truncated. Only 6 byte addresses are used as MAC address now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port Scanner counters after a rescan of a canceled scan (final review, #1) The counters of the other hosts were computed as if every rescanned host had been scanned with all ports: closed = scanned - rescanned hosts x ports - open. After a canceled scan (a host scanned only partly) closed was too low during and after the rescan, and the scanned counter didn't match open + closed. Closed ports are not kept as rows unless they are shown, so the scanner counts them per host now (GetPortsClosedPerHost); the view model subtracts the open and closed ports of the rescanned hosts, and after the rescan adds its scanned ports. Same idea as review 6 #1 in the IP Scanner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner Copy of running probes disabled (final review, #3) The grid shows "..." for a probe that is still running and the export writes an empty value (review 6 #8), but Copy > Ping status / Timestamp / Time / TTL / Bytes and Port status / Ports copied the placeholders as result (e.g. port status "Closed" while the ports were still scanned, ping status "Unknown"). These items are disabled until the ping / port scan of the host is finished. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: IP Scanner live filtering only while searching (final review, #5) Live filtering was always enabled: the view keeps a live shaping list with a listener per row for Hostname, MAC address, Vendor and Ports and filters a row again on every update - also without a search, when every row is shown anyway (e.g. 65,536 rows with Show unreachable). It is enabled in ApplySearch while the search box has text, and disabled again when it is cleared. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port Scanner rescan of a large result with one reset (final review, #6) ReplaceResults removed / added up to 1000 rows one by one regardless of the size of the result, but every single change searches the whole collection and the sorted, grouped view: 500 rows of a result with ~1 M rows (closed ports shown) froze the window. The limit is the effort now: changed rows x all rows (max. 10 M, e.g. 1,000 of 10,000 rows), above it one reset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: IP Scanner rescan with one row per IP address (final review, #7) Since 9f45080a3 every IP address is scanned once, so it has exactly one row. The rescan maps IP address -> list of rows and result -> list of rows (and the loops over them) supported a case that can't happen any more: one row per IP address / result now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: IP Scanner rescan options with "with" (final review, #8) CreateRescanOptions copied all 15 options by hand to set ReportAllHosts - every new option had to be added there too, and a wrong copy (e.g. a current setting instead of the option of the last scan) would compile. IPScannerOptions is a record now, the rescan options are "options with { ReportAllHosts = true }". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port Scanner groups the results per IP address (PS-B1) The results were grouped by the hostname (the IP address only without one): different IP addresses with the same PTR name (e.g. a generic name of a provider, or a server with two IP addresses) were shown in one group, and the group header didn't show any of the IP addresses. The group header is "hostname (IP address)" now (the IP address without hostname) - one group per IP address, which is scanned only once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port Scanner port profile edit / delete only with a selection (PS-B2) Edit and Delete of a port profile in the settings had no CanExecute and the selection started as an empty profile that is not in the list: with no profile selected (e.g. all deleted), F2 / double-click opened Edit with an empty profile and OK added a new one, Del asked to delete nothing. Both need a selected profile now (like the custom commands of the IP Scanner, B15), the delete confirmation names the profile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: Port Scanner scans more hosts at once with few ports (PS-P1) The number of hosts scanned at the same time was fixed (host threads, default 4, max. 10), independent of the ports per host: with few ports the port threads stayed unused - a /24 with one port (e.g. 3389) had 4 connections in flight, 64 rounds x 2 s timeout = about 2 minutes for an empty range (the IP Scanner needs about 4 s). If the hosts have fewer ports than port threads, more hosts are scanned at the same time within the same number of connections (host threads x port threads, default 512), at most 128 hosts (concurrent PTR lookups, like the IP Scanner): /24 x 1 port -> 128 hosts, about 2 timeouts; 1-1024 unchanged (4 hosts). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: Port Scanner counts timed out ports separately (PS-B3, PS-C2) The Closed counter next to the Result header counted closed and timed out ports, while the result and the docs distinguish closed (the host refused the connection) and timed out (no answer): a host behind a firewall showed "0 Open · 1024 Closed" with every row "Timed out". The counter is "Open · Closed · Timed out" now (new string XTimedOut). The scanner counts timed out ports separately, also per host for the rescan of some hosts. Docs: Show closed ports shows closed and timed out ports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port Scanner crash in the port profiles dialog without a profile (PS-B4) Without a shown profile (all port profiles deleted, or the search has no match) the selection was set to [null]. The OK button is disabled then (via the grid's selection count), but Enter in the grid runs OKCommand directly: the OK callback (async lambda) joined the ports of the selection - NullReferenceException in an async void, the application crashed. The selection is empty instead of [null], OKCommand needs a selected profile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port Scanner port profiles with own collection views (PS-B5) The settings and the port profiles dialog showed the port profiles through the default view of the settings collection: every dialog / settings view model added another sort by name to the shared view (the sort keys piled up) and the dialog had to remove its search filter on close, so it didn't filter the settings list. Both use an own view now (like the application list of the main window). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Port Scanner setting to adjust the threads automatically (PS-P1, D-PS1) Replaces the scaling of 3ae408df9, which made "Max. concurrent host threads" a minimum (up to 128 hosts also with 1 host thread set, a hidden limit). New setting "Adjust threads automatically" (default on, help icon): up to 512 connections at the same time (the old defaults 4 x 128), at most 128 hosts and 128 ports per host - 4 hosts for 1-1024, 128 hosts for one port (/24 x 3389: ~4 s instead of ~2 min). Disabled, Max. concurrent host / port threads are used exactly (fields disabled while it is on); host threads up to 256 (was 10), host threads x port threads at most 8,192 connections (half of the local ports, like the IP Scanner maximum 256 x 32) - the maximum of one field follows the other, help icon explains it. Update: customized threads (not 4 / 128) disable the new setting, so they still apply. The PTR lookup of a host starts with its first port probe, so the lookups of many hosts follow the ramp-up instead of starting at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Port Scanner context menu opens the host in other tools (PS-C1, PS-M1) The context menu of a result only had Rescan, Web Console, Copy and Export. Added the host tools of the IP Scanner (same items, icons and order): Ping Monitor, Traceroute, DNS lookup... (IP address / hostname), RDAP... (IP address / registered domain of the hostname), Remote Desktop, PowerShell, PuTTY, TigerVNC and SNMP. Target: the hostname (always a DNS name from the PTR record in the Port Scanner), otherwise the IP address; Web Console uses the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Port Scanner Add profile from a result (PS-M2) Context menu "Add profile..." like the IP Scanner: the profile dialog opens with the host (IP address), the hostname as name (single-label names without the trailing dot) and the Port Scanner enabled with the ports of the shown scan as entered (e.g. "22; 80; 443"). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Port Scanner group header with the number of open ports (PS-M3) Each host group shows its open ports next to the name ("server (10.0.0.1) · 3 Open", translated as one text like the counters), also while the scan runs - useful with many hosts collapsed. The view model counts the open ports per IP address when a row is added (OpenPortsPerHost, also for the rows replaced by a rescan), the converter only looks the host up: WPF raises ItemCount for every added row, counting the rows of the group there would take rows x rows (1-65535 with closed ports shown). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Port Scanner port profiles by name in the ports input (D1) The ports input accepts names of port profiles, also several and together with ports (e.g. "Webserver; Database; 8080", case-insensitive): every ";" entry is a port, a port range or a port profile, resolved when the scan starts (PortProfile.TryResolvePorts, unit tests). Validated by PortsOrPortProfilesValidator in the tool and the run command, which passes the input as entered ("portscan host webserver;database;22") - its port profile suggestions also complete the entry after the last ";". Port profile names must be unique, must not contain ";" and must not be a port or port range (PortProfileNameValidator, IsNameUnique); the update replaces ";" in existing names with ",". PortRangeHelper.IsValidPortOrPortRange: one port or range, "1-2-3" is invalid (was accepted and scanned 1-2, PS-B6 for this input). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Port Scanner ports input with port profiles and history (D2) The Ports input is an editable ComboBox like the DNS server of DNS Lookup: the list shows the port profiles (name and ports, sorted) and the history, grouped (PortScannerPortsSuggestions, updated when the port profiles or the history change - weak events, no leak of closed tabs / dialogs). Selecting a port profile inserts its name, resolved when the scan starts (D1); several port profiles can be typed. Removed the "Select port profile" button and its dialog (PortProfilesChildWindow, PortProfilesViewModel) - also the changelog entries of fixes in that dialog (it doesn't exist in the release). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Chore: Port Scanner settings comment after the port profiles dialog was removed (D2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Port Scanner port profiles with an internal ID (D3) Profiles will reference port profiles by an ID instead of the name, so a renamed port profile is still found (D4). New port profiles get a new Guid, editing keeps it (the settings replace the edited profile), the update adds an ID to existing port profiles. GlobalAssemblyInfo version 2026.10.11.0 -> 2026.10.27.0, so the update (UpgradeToLatest) runs on a development machine with settings of the last build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Profiles reference port profiles for the Port Scanner (D4) The Ports of the Port Scanner in the profile dialog are the same ComboBox as in the tool (port profiles and history, shared templates in ComboBoxStyles.xaml) - replaces B4 without a nested dialog. Saved: PortScanner_PortsInput = the input with port profiles as references to their ID ("{guid}; 8080", PortProfile.EncodeReferences), null without a port profile; PortScanner_Ports = the resolved ports (also read by older versions). Opened / scanned: the references with the current names (TryDecodeReferences, also after a rename); if a port profile can't be found (deleted, or a profile file shared from another computer - port profiles are settings) the saved ports, and the tab shows a note (also with the first scan). Unit tests for encode / decode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Chore: Remove the strings of the removed port profiles dialog (D2) OpenPortProfilesDots (button tooltip) and SelectPortProfile (dialog title) are no longer used since the "Select port profile" button and its dialog were replaced by the ports input (d403a65fc). English only, the translations follow via Transifex. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Shared ping probe for the IP Scanner and Port Scanner (C2) The ping of the IP Scanner (attempts, retry only on a timeout) is moved unchanged into PingProbe (like PortProbe), so the Port Scanner can ping the hosts before the scan with the same logic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Port Scanner option to ping hosts before the scan (C2, PS-M5) New setting "Ping hosts before the scan" (default off, help icon): every host is pinged first (PingProbe, with the timeout and attempts of the port scan, retry only on a timeout); a host that doesn't answer is not scanned, its ports are counted as scanned for the progress. After the scan the number of skipped hosts is shown below the input. Sparse ranges with many ports no longer wait for the timeout of every port of an unused address; hosts that block ICMP are skipped (warning in the docs and the tooltip). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Run command list with a quoted entry, e.g. "DNS (via TCP)";Webserver (D1) A value in quotes ended the word: `portscan host "DNS (via TCP)";Webs` (after completing a port profile with spaces and typing "; Webs") was the argument "DNS (via TCP)" plus an unknown argument ";Webs" - no port profile suggestions. In an open quote ("DNS (via TCP); Webs) the suggestions were shown, but the completion used the value without quotes, so the quotes of the previous entries were lost. Parser: a word consists of parts in quotes and parts without spaces ("DNS (via TCP)";Webs is one word, value DNS (via TCP);Webs); the word as typed is CurrentToken. Completion: the previous entries are kept as typed (an open quote is closed before the ";"), names with spaces are quoted in any entry. Parser unit tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Port Scanner Tab completion of port profiles in the ports input (D1) Like the run command: Tab completes the entry that is being typed with the name of a port profile, the entries before are kept as entered ("webserver; dat" -> "webserver; Database"). Names that start with the entry first, then names that contain it (case-insensitive); Tab again shows the next match, after the last one Tab moves the focus. Without anything to complete (empty entry, port, no match, already the name) Tab moves the focus as usual. A rest the ComboBox autocompleted and selected is ignored. ComboBoxListCompletionBehavior (Controls, now references Utilities) on the ports input of the tool and the profile dialog; the matching is in ListEntryCompletionHelper (unit tests). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Generic input suggestions with profiles and history (E0) PortScannerPortsSuggestions -> InputSuggestions (profiles with a description + history, grouped, profile names for the Tab completion), created per input with a factory (ForPortScannerPorts), so the DNS server input of DNS Lookup can use the same list. Template PortScannerPortsSuggestionTemplate -> InputSuggestionTemplate (Description instead of Ports). No behaviour change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: DNS Lookup several DNS servers and DNS server names in a list (E1) The DNS server input accepts several entries separated by ";": names of DNS server profiles (case-insensitive) and DNS servers (IP address or hostname, optional port), e.g. "Google Public DNS; Cloudflare; 9.9.9.9". Shared resolver DNSServer.ResolveServers (every server once, [Windows DNS] alone uses the Windows resolver as before, in a list it adds the DNS servers of Windows) and GetInvalidEntries for the validators; unit tests. Fixes: the tool matched the name case-sensitively while the run command validated it case-insensitively - "dns host cloudflare" was resolved as hostname "cloudflare". Run command: every entry is validated (custom servers still need an IP or a domain), suggestions also after a ";". DNS server names must not contain ";" (validator in the server profile dialog, shared with SNTP; the update replaces ";" with ","), the name of [Windows DNS] counts as used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: DNS Lookup DNS server input with history and Tab completion (E2) The DNS server ComboBox shows the DNS servers (Windows DNS first, then by name, with their servers) and a new history (DNSLookup_DNSServerHistory), grouped like the ports of the Port Scanner (InputSuggestions); Tab completes the name of a DNS server that is being typed (ComboBoxListCompletionBehavior), the input is validated (DNSServersValidator). The SelectedItem binding to a profile is gone - the input is resolved by name (E1). A single profile name is not added to the history, it is already in the list (DNS Lookup and Port Scanner). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: DNS Lookup DNS servers with an internal ID (E3) Profiles will reference DNS servers by an ID instead of the name, so a renamed DNS server is still found (E4). New DNS servers (and the default Windows DNS server) get a new Guid, editing keeps it, the update adds an ID to existing DNS servers. The Windows DNS server will be referenced by name (it exists on every computer, with another ID). GlobalAssemblyInfo 2026.10.27.0 -> 2026.10.27.1, so the update runs again on a development machine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Profile DNS Lookup "Inherit host" saved with the Traceroute value ParseProfileInfo set DNSLookup_InheritHost from Traceroute_InheritHost (copy & paste). A profile with an own DNS Lookup host and Traceroute inheriting the host was saved with DNS Lookup inheriting too - the next save replaced the DNS Lookup host with the host of the profile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Profiles can override the DNS server of DNS Lookup (E4) New profile option "DNS server" (DNS Lookup tab, checkbox + the same ComboBox as the tool: DNS servers + history, Tab completion, validation; tab error indicator / OK disabled on an invalid input). Saved like the ports of the Port Scanner (D4): DNSLookup_DNSServerInput = the input with DNS servers by name as references to their ID ("{guid}; 9.9.9.9", DNSServer.EncodeReferences; [Windows DNS] by name, its ID differs per computer), null without a DNS server by name; DNSLookup_DNSServer = the input with the servers of the DNS servers (DNSServer.ReplaceProfilesByServers). Opened / looked up: the references with the current names; if a DNS server can't be found (deleted, or a profile file shared from another computer) the saved servers, and the tab shows a note (also with the first lookup). Unit tests for encode / decode / replace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test: Run command with several DNS servers by name (E1) Parser tests for "dns host "Google Public DNS";cloudflare;9.9.9.9 aaaa" (with / without spaces around ";", type before or after) and for completing the entry after the last ";" of a DNS server list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: History first and profiles in two lines in the ports / DNS server input The drop down of the ports (Port Scanner, profile) and DNS server (DNS Lookup, profile) input looks like the suggestions of the run command: group headers in Gray3 with more space, a profile in two lines (name, then its ports / IP addresses in gray, trimmed with a tooltip), the items as wide as the ComboBox (the drop down grew with a long description). The history comes first: few entries (at most the history size, custom inputs only - a single profile name isn't added), visible without scrolling below 12+ profiles, and the ComboBox's own autocomplete prefers what was used last. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Chore: GitHub issue forms and simplified pull request template Bug report, feature request and question are issue forms now (affected feature, version, installation, architecture, Windows version, log) with contact links to the docs, discussions, Transifex and security advisories. Blank issues are disabled. The pull request template drops the Copilot summary and gets a type of change, a test section and the repo checklist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner host duration of the start scheduler overflows int (review #4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Web Console URL with the trailing dot of a single-label hostname (review #5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner reads the neighbor table for an unreachable host if it was not read yet (review #6) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port Scanner message of the tab still shown after the ports were changed (review #7) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Improvement: Port Scanner pings hosts before the scan with their own concurrency if threads are adjusted automatically (review #2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Shared name / ID reference input for port profiles and DNS server profiles (review #8) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Shared search delay instead of a search timer in every view model (review #9) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Revert: IP Scanner reads the neighbor table for an unreachable host if it was not read yet (final review #1) An unreachable host that blocks ICMP still has an ARP entry from the ping, the first read of the table finds its MAC address. Comments describe this now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port Scanner hid an error of a scan that didn't start when the ports were changed (final review #2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Port Scanner ping mode computed once, ping pipeline without Task.Run and a second token (final review #5, #6) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Search delay provides the command to apply the search right away (final review #7) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Hostname without the trailing dot only for applications that resolve it via Windows, shared helper (final review #3, #4) DNSClientHelper.SanitizeHostname removes the trailing dot (also of a single-label name like "gateway."). ApplicationManager.GetHostForApplication uses it for Remote Desktop, PuTTY, TigerVNC, PowerShell and the Web Console; tools that resolve via NETworkManager keep "gateway." so the DNS suffix is not appended. Used by the IP Scanner, Port Scanner and Traceroute, the Web Console URL, custom commands and the profile name. WebConsoleUrlHelper.CreateUrl throws for a missing host instead of a NullReferenceException. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port Scanner message of the tab replaced by a message of the first scan (final final review #1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Target prepared for the application in NavigationManager.Open instead of every caller (final final review #2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Docs: Changelog trailing dot of single-label names when opened in another feature (final final review #3) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Web Console not in the applications whose host is sanitized, its target is a URL (final final review #4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Sanitized hostname of only dots is empty, profile name falls back to the IP address (final final review #5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: Ping Monitor uses the shared helper to remove the trailing dot of the hostname (final final review #6) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refactor: TrimTrailingDot builds on SanitizeHostname (final final review #7) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Web Console URL checks the host before the open ports (final final review #8) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner / Port Scanner don't start the scan if no host is left after resolving the hostnames (B8) Only "The following hostnames could not be resolved" is shown instead of also "No reachable hosts found" / "No open ports found", and the input is not added to the history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: ICMP buffer above 65500 lowered on every load of the settings, not only on an upgrade A settings file edited (or restored) after the upgrade to the current version kept a buffer of e.g. 65535, which makes .NET's Ping throw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Revert "Fix: ICMP buffer above 65500 lowered on every load of the settings, not only on an upgrade" This reverts commit a47e4b14a. The buffer is lowered inline in UpgradeToLatest again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Column that fills the remaining width could switch between two widths in a loop and hang the window DataGridHelper.FillRemainingWidth: a horizontal scrollbar can make the vertical one appear, the narrower fill column removes both, the wider one shows the horizontal scrollbar again ... Each update is queued with Loaded priority (before input), so the window hangs without an exception. Going back to the wider width of two updates ago within 500 ms (without a size change of the grid) keeps the narrower width. Seen once in a Port Scanner run 1-65535 (stuck in the middle, no exception); cause not confirmed with a stack trace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: IP Scanner settings delete several selected custom commands at once The custom commands grid allowed selecting several rows, but Edit / Delete only used one of them. Like the profiles: MultiSelectDataGrid with the selected commands, a context menu with Delete for several rows, Del deletes all selected after a confirmation that lists them, Edit (F2, context menu) only with exactly one selected command. New strings DeleteCustomCommands / DeleteCustomCommandsMessage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: Port Scanner JSON export uses the key State like CSV / XML instead of Status (breaking, PS-C3) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: IP Scanner settings button to restore the default ports Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Port Scanner settings delete several selected port profiles at once Like the custom commands of the IP Scanner: MultiSelectDataGrid, context menu with Delete for several rows, Del deletes all selected after a confirmation that lists them, Edit only with exactly one. The first port profile is still selected on open and after deleting. New strings DeletePortProfiles / DeletePortProfilesMessage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: IP Scanner default custom commands Windows Explorer (shares), Computer Management, Event Viewer and System Information Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Port Scanner rescan of the selected ports in place, Rescan host in the group header Rescan in the row context menu rescanned all ports of the selected hosts (e.g. all 65535 to check one port). Now: - Rows: Rescan scans only the selected ports (PortScanner.ScanPortsAsync, per host only its ports) and updates their state in place (PortInfo.State observable); selection, collapsed groups and the scroll position stay. Every rescanned port is reported (a port closed now keeps its row with the new state, like a host down now in the IP Scanner), without ping befo…
1 parent b20d60d commit 62fc57f

142 files changed

Lines changed: 7768 additions & 1570 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude/skills/feature-review/SKILL.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ Reference code as `path:line`. Rate each finding (High / Medium / Low) in the fi
147147
prioritize. Proposed fixes describe the smallest change that follows the existing patterns.
148148

149149
For larger missing features, add an issue draft at the end that follows the repository's feature request
150-
template (`.github/ISSUE_TEMPLATE/Feature_request.md`): a short title, "Describe your feature request",
151-
the motivation, a proposed behaviour (UI + logic), and which existing feature it could be modelled on.
150+
form (`.github/ISSUE_TEMPLATE/feature_request.yml`): a short title, the affected feature, "Problem / motivation",
151+
"Proposed solution" (UI + logic), and "Alternatives / workarounds" incl. which existing feature it could be modelled on.
152152

153153
### 7. After the report
154154

‎.claude/skills/feature-review/references/conventions.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ Reference: `HostsFileEditorView.xaml`, `FirewallView.xaml`.
102102
Other users: `RunCommandViewModel.Suggestions`. Not yet used (same `Clear()` + `ForEach(Results.Add)` pattern on every
103103
refresh / auto refresh): `ConnectionsViewModel`, `ListenersViewModel`, `NeighborTableViewModel`,
104104
`HostsFileEditorViewModel` — report as a performance finding when reviewing them.
105+
- Column widths: no star (`Width="*"`) columns in a `DataGrid` — with a visible star column WPF only lets a column
106+
grow by taking the width from the columns to its right, so columns can't be made wider than the window (#3557).
107+
Use `wpfHelpers:DataGridHelper.FillRemainingWidth="True"` on the grid: the last visible column (or the one marked with
108+
`wpfHelpers:DataGridHelper.IsFillColumn="True"`) fills the remaining width with a pixel width, every column can be
109+
resized freely and the grid scrolls horizontally. Give the fill column a `MinWidth`.
110+
- Bindings on items that implement `INotifyPropertyChanged` (e.g. `IPScannerHostInfo`, view models via `BindingProxy`)
111+
use **plain paths** (`IsPingPending`, `PingInfo.Status`, `Data.ScanOptions.ResolveHostname`), not the typed form
112+
`(network:Type.Property)`. WPF registers a `PropertyChanged` listener with the property name the path parser stored;
113+
for a typed part that name stays `null` if the owner type can't be resolved early (templates, child bindings of a
114+
`MultiBinding`) → `ArgumentNullException` (`Parameter 'key'`) in `PropertyChangedEventManager.AddListener` as soon as
115+
the item is shown. The typed form is only safe on plain (non-notifying) items.
105116
- Don't use a `Reset` for results that are *appended* while a scan runs (IP Scanner, Port Scanner, Traceroute): with a
106117
sorted view every `Reset` re-sorts the whole list and the grid loses its selection / scroll position. Those tools buffer
107118
results and flush them with a `DispatcherTimer` (reference: `IPScannerViewModel.FlushResultsBuffer`).
@@ -130,6 +141,12 @@ Reference: `HostsFileEditorView.xaml`, `FirewallView.xaml`.
130141

131142
- Tool view: `Grid Margin="10"`, 10px spacer rows/columns. Header row: `HeaderTextBlock` left, refresh
132143
(`CleanButton` with `ReloadAnimationHelper.IsReloading`) and search (`SearchTextBox`, width 250) right.
144+
- Search box (`SearchTextBox`): filters while typing. Lists that can be large or slow to filter use a delay
145+
(`DispatcherTimer` with `GlobalStaticConfiguration.SearchDispatcherTimerTimeSpan`, reference: `FirewallViewModel`),
146+
short fixed lists filter on every key press. `Enter` only filters: `wpfHelpers:SearchTextBoxHelper.EnterCommand`
147+
(delayed search, e.g. `ApplySearchCommand` = stop the timer + `Refresh()`) or `SearchTextBoxHelper.HandleEnter="True"`
148+
(immediate search), so `Enter` never triggers the default button of the view / dialog / window. Exception: the
149+
profile panel search runs the selected profile on `Enter` (`ProfileExpanderPanel`).
133150
- Bottom row: secondary actions left (e.g. Open hosts file, Windows Firewall settings), primary action right
134151
(e.g. Add entry...), `ImageWithTextButton`.
135152
- Dialog: two-column grid (`1*`, 10, `2*`), 10px spacing between rows, OK (`HighlightedButton`, `IsDefault`)

‎.github/ISSUE_TEMPLATE/Bug_report.md‎

Lines changed: 0 additions & 28 deletions
This file was deleted.

‎.github/ISSUE_TEMPLATE/Feature_request.md‎

Lines changed: 0 additions & 7 deletions
This file was deleted.

‎.github/ISSUE_TEMPLATE/Question.md‎

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
name: 🐛 Bug report
2+
description: Something doesn't work as expected, or the application crashes.
3+
labels: [":mag: needs triage"]
4+
body:
5+
- type: markdown
6+
attributes:
7+
value: |
8+
Thanks for taking the time to report a bug! 🙏
9+
10+
Please check the [existing issues](https://github.com/BornToBeRoot/NETworkManager/issues?q=is%3Aissue) first and whether the bug still happens with the [latest version](https://github.com/BornToBeRoot/NETworkManager/releases) (or pre-release).
11+
12+
> [!IMPORTANT]
13+
> **Security vulnerabilities** must not be reported here - please use a [private security advisory](https://github.com/BornToBeRoot/NETworkManager/security/advisories/new).
14+
15+
- type: dropdown
16+
id: area
17+
attributes:
18+
label: Affected feature
19+
description: Where does the bug happen?
20+
options:
21+
- General (start, settings, profiles, update, ...)
22+
- Run command
23+
- Dashboard
24+
- Network Interface
25+
- WiFi
26+
- IP Scanner
27+
- Port Scanner
28+
- Ping Monitor
29+
- Traceroute
30+
- DNS Lookup
31+
- Remote Desktop
32+
- PowerShell
33+
- PuTTY
34+
- TigerVNC
35+
- Web Console
36+
- SNMP
37+
- SNTP Lookup
38+
- Hosts File Editor
39+
- Firewall
40+
- Discovery Protocol
41+
- Wake on LAN
42+
- RDAP
43+
- Whois
44+
- IP Geolocation
45+
- Subnet Calculator
46+
- Bit Calculator
47+
- Lookup
48+
- Connections
49+
- Listeners
50+
- Neighbor Table
51+
- Documentation / website
52+
- Other
53+
validations:
54+
required: true
55+
56+
- type: textarea
57+
id: description
58+
attributes:
59+
label: What happened?
60+
description: Describe the bug and what you expected to happen instead.
61+
placeholder: When I scan a range, hosts are shown as down, but I expected them to be up because ...
62+
validations:
63+
required: true
64+
65+
- type: textarea
66+
id: steps
67+
attributes:
68+
label: Steps to reproduce
69+
description: How can the bug be reproduced? Include the input and settings you used (without sensitive data).
70+
placeholder: |
71+
1. Open the IP Scanner
72+
2. Enter `10.0.0.0/24` and click **Scan**
73+
3. ...
74+
validations:
75+
required: true
76+
77+
- type: input
78+
id: version
79+
attributes:
80+
label: NETworkManager version
81+
description: Shown in **Settings > About**.
82+
placeholder: e.g. 2026.7.7.0
83+
validations:
84+
required: true
85+
86+
- type: dropdown
87+
id: installation
88+
attributes:
89+
label: Installation
90+
options:
91+
- Setup (MSI)
92+
- Portable
93+
- Archive
94+
- WinGet
95+
- Chocolatey
96+
- Built from source
97+
validations:
98+
required: true
99+
100+
- type: dropdown
101+
id: architecture
102+
attributes:
103+
label: Architecture
104+
options:
105+
- x64
106+
- ARM64
107+
validations:
108+
required: true
109+
110+
- type: input
111+
id: windows
112+
attributes:
113+
label: Windows version
114+
description: Run `winver` to see the version and build.
115+
placeholder: e.g. Windows 11 24H2 (Build 26100.4061)
116+
validations:
117+
required: true
118+
119+
- type: input
120+
id: dotnet
121+
attributes:
122+
label: .NET Desktop Runtime version
123+
description: Only needed if the application doesn't start - run `dotnet --list-runtimes`.
124+
placeholder: e.g. Microsoft.WindowsDesktop.App 10.0.0
125+
126+
- type: textarea
127+
id: logs
128+
attributes:
129+
label: Error message / log
130+
description: |
131+
The error message shown by NETworkManager, or the relevant part of the log file `%LocalAppData%\NETworkManager\NETworkManager.log`.
132+
If the application crashes, also the error in the Event Viewer (**Windows Logs > Application**).
133+
render: text
134+
135+
- type: textarea
136+
id: screenshots
137+
attributes:
138+
label: Screenshots / additional information
139+
description: Drag & drop screenshots or files here, or add anything else that helps to understand the bug.
140+
141+
- type: checkboxes
142+
id: checklist
143+
attributes:
144+
label: Checklist
145+
options:
146+
- label: I have searched the existing issues and this bug hasn't been reported yet.
147+
required: true
148+
- label: I have removed sensitive data (e.g. passwords, IP addresses, hostnames) from logs and screenshots.
149+
required: true

‎.github/ISSUE_TEMPLATE/config.yml‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
blank_issues_enabled: false
2+
contact_links:
3+
- name: 📖 Documentation
4+
url: https://borntoberoot.net/NETworkManager/docs/introduction
5+
about: Features, settings, FAQ and how to use NETworkManager.
6+
- name: 💬 Discussions
7+
url: https://github.com/BornToBeRoot/NETworkManager/discussions
8+
about: Share ideas, show what you built or talk with the community.
9+
- name: 🌍 Translations
10+
url: https://app.transifex.com/BornToBeRoot/NETworkManager/dashboard/
11+
about: Translations are managed on Transifex - help translate NETworkManager there.
12+
- name: 🔒 Security vulnerability
13+
url: https://github.com/BornToBeRoot/NETworkManager/security/advisories/new
14+
about: Please report security vulnerabilities privately, not as a public issue.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
name: 💡 Feature request
2+
description: Suggest a new feature or an improvement of an existing one.
3+
labels: [":rocket: feature-request"]
4+
body:
5+
- type: markdown
6+
attributes:
7+
value: |
8+
Thanks for helping to make NETworkManager better! 🚀
9+
10+
Please check the [existing issues](https://github.com/BornToBeRoot/NETworkManager/issues?q=is%3Aissue) first - maybe your idea was already suggested. If so, give it a 👍 instead of creating a new issue.
11+
12+
- type: dropdown
13+
id: area
14+
attributes:
15+
label: Feature
16+
description: Which feature is it about?
17+
options:
18+
- New feature / tool
19+
- General (start, settings, profiles, update, ...)
20+
- Run command
21+
- Dashboard
22+
- Network Interface
23+
- WiFi
24+
- IP Scanner
25+
- Port Scanner
26+
- Ping Monitor
27+
- Traceroute
28+
- DNS Lookup
29+
- Remote Desktop
30+
- PowerShell
31+
- PuTTY
32+
- TigerVNC
33+
- Web Console
34+
- SNMP
35+
- SNTP Lookup
36+
- Hosts File Editor
37+
- Firewall
38+
- Discovery Protocol
39+
- Wake on LAN
40+
- RDAP
41+
- Whois
42+
- IP Geolocation
43+
- Subnet Calculator
44+
- Bit Calculator
45+
- Lookup
46+
- Connections
47+
- Listeners
48+
- Neighbor Table
49+
- Documentation / website
50+
- Other
51+
validations:
52+
required: true
53+
54+
- type: textarea
55+
id: problem
56+
attributes:
57+
label: Problem / motivation
58+
description: What do you want to achieve, or what is missing or annoying today?
59+
placeholder: I often need to ... but currently I have to ...
60+
validations:
61+
required: true
62+
63+
- type: textarea
64+
id: solution
65+
attributes:
66+
label: Proposed solution
67+
description: How should it work? Describe the behavior and the UI (e.g. button, setting, context menu entry).
68+
placeholder: Add a ... to the ... that ...
69+
validations:
70+
required: true
71+
72+
- type: textarea
73+
id: alternatives
74+
attributes:
75+
label: Alternatives / workarounds
76+
description: Other solutions you have considered, or how other tools solve it.
77+
78+
- type: textarea
79+
id: context
80+
attributes:
81+
label: Additional information
82+
description: Screenshots, mockups, links or anything else that helps.
83+
84+
- type: checkboxes
85+
id: checklist
86+
attributes:
87+
label: Checklist
88+
options:
89+
- label: I have searched the existing issues and this feature hasn't been requested yet.
90+
required: true
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: ❓ Question
2+
description: Ask a question about NETworkManager or how to use a feature.
3+
labels: [":question: question"]
4+
body:
5+
- type: markdown
6+
attributes:
7+
value: |
8+
Have a question? Maybe the [documentation](https://borntoberoot.net/NETworkManager/docs/introduction) or the [FAQ](https://borntoberoot.net/NETworkManager/docs/category/faq) already has the answer. For ideas and general discussions, you can also use the [discussions](https://github.com/BornToBeRoot/NETworkManager/discussions).
9+
10+
- type: textarea
11+
id: question
12+
attributes:
13+
label: Question
14+
description: What would you like to know?
15+
validations:
16+
required: true
17+
18+
- type: textarea
19+
id: context
20+
attributes:
21+
label: Context
22+
description: What are you trying to achieve, and what have you tried? Screenshots are welcome.
23+
24+
- type: input
25+
id: version
26+
attributes:
27+
label: NETworkManager version
28+
description: Shown in **Settings > About** (if the question is about the application).
29+
placeholder: e.g. 2026.7.7.0

0 commit comments

Comments
 (0)