diff --git a/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor.css b/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor.css
index 2f06ea9..56e7222 100644
--- a/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor.css
+++ b/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor.css
@@ -211,6 +211,11 @@
word-break: break-word;
}
+/* Row-level ok/cancel (and revert) for multi-value: right-align on the row below the list */
+::deep .ec-field .ec-field-value-multi + .status-badge-inline {
+ margin-left: auto;
+}
+
::deep .ec-field-value-list {
margin: 0;
padding-left: 1.25rem;
diff --git a/src/DnsmasqWebUI/Infrastructure/Helpers/Config/EffectiveConfigOptionKindMap.cs b/src/DnsmasqWebUI/Infrastructure/Helpers/Config/EffectiveConfigOptionKindMap.cs
index 113cc2a..447859b 100644
--- a/src/DnsmasqWebUI/Infrastructure/Helpers/Config/EffectiveConfigOptionKindMap.cs
+++ b/src/DnsmasqWebUI/Infrastructure/Helpers/Config/EffectiveConfigOptionKindMap.cs
@@ -286,7 +286,7 @@ public static class EffectiveConfigParserBehaviorMap
[DnsmasqConfKeys.LogDhcp] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.KeepInForeground] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.NoDaemon] = EffectiveConfigParserBehavior.Flag,
- [DnsmasqConfKeys.Conntrack] = EffectiveConfigParserBehavior.LastWins,
+ [DnsmasqConfKeys.Conntrack] = EffectiveConfigParserBehavior.Flag,
};
/// Returns LastWins, Flag, or Multi for the given option name; defaults to LastWins if unknown.
diff --git a/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/ConfigSetCache.cs b/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/ConfigSetCache.cs
index 5f7644a..a9ea0e9 100644
--- a/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/ConfigSetCache.cs
+++ b/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/ConfigSetCache.cs
@@ -391,8 +391,7 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
var dhcpScriptPath = string.IsNullOrWhiteSpace(dhcpScriptVal) ? null : DnsmasqConfIncludeParser.ResolvePath(dhcpScriptVal?.Trim(), dhcpScriptDir);
var (mxTargetVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.MxTarget);
var mxTarget = string.IsNullOrWhiteSpace(mxTargetVal) ? null : mxTargetVal.Trim();
- var (conntrackVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.Conntrack);
- var conntrack = string.IsNullOrWhiteSpace(conntrackVal) ? null : conntrackVal.Trim();
+ var conntrack = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.Conntrack);
return new EffectiveDnsmasqConfig(
noHosts, addnHosts, hostsdirPath,
@@ -535,7 +534,7 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
var (_, fastDnsRetrySource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.FastDnsRetry, managedFilePath);
var (_, dhcpScriptPathSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpScript, managedFilePath);
var (_, mxTargetSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.MxTarget, managedFilePath);
- var (_, conntrackSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.Conntrack, managedFilePath);
+ var (_, conntrackSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.Conntrack, managedFilePath);
return new EffectiveConfigSources(
noHostsSource, addnHostsWithSource.Select(t => new PathWithSource(t.Path, t.Source)).ToList(), hostsdirPathSource,
@@ -718,7 +717,7 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
LogFacility: null, LogQueries: null, AuthTtl: null, EdnsPacketMax: null, QueryPort: null, PortLimit: null, MinPort: null, MaxPort: null, LogAsync: null, LocalService: null,
DhcpLeaseMax: null, NegTtl: null, MaxTtl: null, MaxCacheTtl: null, MinCacheTtl: null, DhcpTtl: null,
TftpRootPath: null, PxePrompt: null, EnableDbus: null, EnableUbus: null, FastDnsRetry: null,
- DhcpScriptPath: null, MxTarget: null, Conntrack: null
+ DhcpScriptPath: null, MxTarget: null, Conntrack: false
);
private static EffectiveConfigSources CreateDefaultEffectiveConfigSources() =>
diff --git a/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/DnsmasqConfigService.cs b/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/DnsmasqConfigService.cs
index 0780c50..391aef8 100644
--- a/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/DnsmasqConfigService.cs
+++ b/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/DnsmasqConfigService.cs
@@ -271,13 +271,23 @@ public class DnsmasqConfigService : IDnsmasqConfigService
if (idx >= 0) list.RemoveAt(idx);
continue;
}
+ if (!isFlag)
+ {
+ var v = ToConfValue(c.NewValue);
+ if (string.IsNullOrWhiteSpace(v))
+ {
+ // Non-flag option with no value: remove existing line if any; never write key-only (malformed).
+ if (idx >= 0) list.RemoveAt(idx);
+ continue;
+ }
+ }
string rawLine;
if (isFlag)
rawLine = confKey;
else
{
- var v = ToConfValue(c.NewValue);
- rawLine = string.IsNullOrEmpty(v) ? confKey : confKey + "=" + v;
+ var v = ToConfValue(c.NewValue).Trim();
+ rawLine = confKey + "=" + v;
}
var newLine = new OtherLine { LineNumber = maxLineNumber + 1, RawLine = rawLine };
maxLineNumber++;
diff --git a/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/EffectiveConfigRenderFragmentRegistry.cs b/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/EffectiveConfigRenderFragmentRegistry.cs
index 74e8e33..d9fd6ec 100644
--- a/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/EffectiveConfigRenderFragmentRegistry.cs
+++ b/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/EffectiveConfigRenderFragmentRegistry.cs
@@ -75,6 +75,7 @@ public class EffectiveConfigRenderFragmentRegistry : IEffectiveConfigRenderFragm
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.LogDhcp);
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.KeepInForeground);
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.NoDaemon);
+ RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.Conntrack);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDnssec, DnsmasqConfKeys.ProxyDnssec);
// log-queries: dropdown (Off / On / extra / proto / auth).
diff --git a/src/DnsmasqWebUI/Models/Dnsmasq/EffectiveConfig/EffectiveDnsmasqConfig.cs b/src/DnsmasqWebUI/Models/Dnsmasq/EffectiveConfig/EffectiveDnsmasqConfig.cs
index 5ccd81d..d4823cb 100644
--- a/src/DnsmasqWebUI/Models/Dnsmasq/EffectiveConfig/EffectiveDnsmasqConfig.cs
+++ b/src/DnsmasqWebUI/Models/Dnsmasq/EffectiveConfig/EffectiveDnsmasqConfig.cs
@@ -139,7 +139,7 @@ public record EffectiveDnsmasqConfig(
string? FastDnsRetry,
string? DhcpScriptPath,
string? MxTarget,
- string? Conntrack
+ bool Conntrack
)
{
///