diff --git a/src/DnsmasqWebUI/Components/Dnsmasq/ConfigFileList.razor b/src/DnsmasqWebUI/Components/Dnsmasq/ConfigFileList.razor
new file mode 100644
index 0000000..a391cf1
--- /dev/null
+++ b/src/DnsmasqWebUI/Components/Dnsmasq/ConfigFileList.razor
@@ -0,0 +1,72 @@
+@namespace DnsmasqWebUI.Components.Dnsmasq
+@using DnsmasqWebUI.Models.Dnsmasq
+@using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig
+
+
+ @{
+ var configFileList = Status.ConfigFiles?.Count > 0 ? Status.ConfigFiles : ConfigSet?.Files;
+ var hasList = configFileList != null && configFileList.Count > 0;
+ var mainEntry = hasList ? configFileList![0] : null;
+ var hasMoreThanOne = hasList && configFileList!.Count > 1;
+ var fileCount = hasList ? configFileList!.Count : 2;
+ var showToggle = hasMoreThanOne || (!hasList && Status.ManagedFilePath != null);
+ }
+
Config files (load order)
+
+
+
+ @if (hasList)
+ {
+
+ }
+ else
+ {
+ - Main config: @(Status.MainConfigPath ?? "—") @(Status.MainConfigPathExists ? "✓" : "(missing)")
+ }
+
+ @if (showToggle)
+ {
+
+
+
+ @if (hasList)
+ {
+ @foreach (var f in configFileList!.Skip(1))
+ {
+
+ }
+ }
+ else
+ {
+ - Managed file: @(Status.ManagedFilePath ?? "—") @(Status.ManagedFilePathExists ? "✓" : "(not created yet)")
+ }
+
+
+
+ }
+
+ @if (showToggle)
+ {
+
+ }
+
+
+
+@code {
+ [Parameter, EditorRequired]
+ public DnsmasqServiceStatus Status { get; set; } = null!;
+
+ [Parameter]
+ public DnsmasqConfigSet? ConfigSet { get; set; }
+
+ private bool _expanded;
+
+ private void ToggleExpanded() => _expanded = !_expanded;
+}
diff --git a/src/DnsmasqWebUI/Components/Dnsmasq/ConfigFileList.razor.css b/src/DnsmasqWebUI/Components/Dnsmasq/ConfigFileList.razor.css
new file mode 100644
index 0000000..c4c0efd
--- /dev/null
+++ b/src/DnsmasqWebUI/Components/Dnsmasq/ConfigFileList.razor.css
@@ -0,0 +1,98 @@
+/* Config file list: card-style block with expandable list */
+.config-file-list {
+ --cfl-radius: var(--app-radius, 0.375rem);
+ --cfl-spacing: var(--app-spacing-md, 1rem);
+ --cfl-border: 1px solid #e9ecef;
+ --cfl-bg: #f8f9fa;
+ --cfl-bg-hover: #e9ecef;
+}
+
+.config-file-list {
+ border-radius: var(--cfl-radius);
+ border: var(--cfl-border);
+ background: var(--cfl-bg);
+ padding: var(--cfl-spacing);
+ margin-top: 0.5rem;
+}
+
+.config-file-list .config-file-list-title {
+ font-size: 0.9375rem;
+ font-weight: 600;
+ color: #212529;
+ margin-bottom: 0.5rem;
+}
+
+.config-file-list ul {
+ margin: 0;
+ padding-left: 1.25rem;
+ font-size: 0.875rem;
+ line-height: 1.6;
+ color: #495057;
+}
+
+.config-file-list ul li {
+ padding: 0.2rem 0;
+}
+
+/* Content and toggle on one row: list grows, toggle at bottom-right */
+.config-file-list-content-row {
+ display: flex;
+ align-items: flex-end;
+ gap: 0.75rem;
+}
+
+.config-file-list-list {
+ flex: 1;
+ min-width: 0;
+}
+
+.config-file-list .config-file-list-toggle {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.35rem;
+ flex-shrink: 0;
+ padding: 0.35rem 0.5rem;
+ font-size: 0.8125rem;
+ font-weight: 500;
+ color: #0d6efd;
+ background: transparent;
+ border: none;
+ border-radius: var(--cfl-radius);
+ cursor: pointer;
+ transition: background-color 0.15s ease, color 0.15s ease;
+}
+
+.config-file-list .config-file-list-toggle:hover {
+ background: var(--cfl-bg-hover);
+ color: #0a58ca;
+}
+
+.config-file-list .config-file-list-toggle:focus-visible {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
+}
+
+.config-file-list .config-file-list-toggle .bi {
+ font-size: 0.875rem;
+ transition: transform 0.2s ease;
+}
+
+/* Expandable section: animate height with grid 0fr → 1fr */
+.config-file-list-expandable {
+ display: grid;
+ grid-template-rows: 0fr;
+ transition: grid-template-rows 0.25s ease-out;
+}
+
+.config-file-list-expandable.expanded {
+ grid-template-rows: 1fr;
+}
+
+.config-file-list-expandable-inner {
+ min-height: 0;
+ overflow: hidden;
+}
+
+.config-file-list-expandable-inner ul {
+ padding-top: 0.15rem;
+}
diff --git a/src/DnsmasqWebUI/Components/Dnsmasq/ConfigFileListItem.razor b/src/DnsmasqWebUI/Components/Dnsmasq/ConfigFileListItem.razor
new file mode 100644
index 0000000..8fc7b41
--- /dev/null
+++ b/src/DnsmasqWebUI/Components/Dnsmasq/ConfigFileListItem.razor
@@ -0,0 +1,30 @@
+@namespace DnsmasqWebUI.Components.Dnsmasq
+@using DnsmasqWebUI.Models.Dnsmasq
+@using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig
+
+
+ @if (Entry.IsManaged)
+ {
+
+ }
+ else
+ {
+
+ }
+ @Entry.Path
+ @{
+ var isMain = string.Equals(Entry.Path, Status.MainConfigPath, StringComparison.Ordinal);
+ var isManaged = Entry.IsManaged;
+ var exists = isMain ? Status.MainConfigPathExists : (isManaged ? Status.ManagedFilePathExists : (bool?)null);
+ }
+ @if (exists == true) { ✓ }
+ @if (exists == false) { @(isManaged ? "(not created yet)" : "(missing)") }
+
+
+@code {
+ [Parameter, EditorRequired]
+ public DnsmasqConfigSetEntry Entry { get; set; } = null!;
+
+ [Parameter, EditorRequired]
+ public DnsmasqServiceStatus Status { get; set; } = null!;
+}
diff --git a/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor.css b/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor.css
index f38ce53..f931d6d 100644
--- a/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor.css
+++ b/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor.css
@@ -125,6 +125,22 @@
opacity: 1;
}
+/* Animated expand/collapse: grid 0fr → 1fr */
+::deep .ec-section-collapse {
+ display: grid;
+ grid-template-rows: 0fr;
+ transition: grid-template-rows 0.25s ease-out;
+}
+
+::deep .ec-section-collapse-open {
+ grid-template-rows: 1fr;
+}
+
+::deep .ec-section-collapse-inner {
+ min-height: 0;
+ overflow: hidden;
+}
+
::deep .ec-section-body {
padding: 0.25rem 1rem 0.5rem 1rem;
padding-left: 1rem;
diff --git a/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSectionPanel.razor b/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSectionPanel.razor
index 9b2c732..3075f51 100644
--- a/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSectionPanel.razor
+++ b/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSectionPanel.razor
@@ -8,8 +8,9 @@
@Title
-
-
+
+
+
@foreach (var f in Descriptors)
{
@@ -18,6 +19,7 @@
IsEditMode="@IsEditMode" IsActive="@(ActiveFieldKey == fieldKey)" />
}
+