From 36212a1321fbe428279fbee3b38d4b4eb090213c Mon Sep 17 00:00:00 2001 From: Teuz Date: Thu, 15 Jan 2026 19:40:18 +0100 Subject: [PATCH 1/4] Improve and fix expand/collapse feature - Refactored "Expand/Collapse All" logic to support dynamic Swagger UI content using retries and MutationObserver. - Prevented duplicate expand/collapse buttons with data attributes. - Updated minified file to reflect all changes. --- .../AspNetCore/Swagger/Themes/Scripts/ui.js | 210 ++++++++++++------ .../Swagger/Themes/Scripts/ui.min.js | 2 +- 2 files changed, 138 insertions(+), 74 deletions(-) diff --git a/src/AspNetCore.Swagger.Themes.Common/AspNetCore/Swagger/Themes/Scripts/ui.js b/src/AspNetCore.Swagger.Themes.Common/AspNetCore/Swagger/Themes/Scripts/ui.js index 24a6672..1055e75 100644 --- a/src/AspNetCore.Swagger.Themes.Common/AspNetCore/Swagger/Themes/Scripts/ui.js +++ b/src/AspNetCore.Swagger.Themes.Common/AspNetCore/Swagger/Themes/Scripts/ui.js @@ -109,47 +109,113 @@ function setUpExpandAndCollapseOperationsButtons(enabled) { if (enabled === false) return; - const opBlockSections = document.querySelectorAll('.opblock-tag-section'); + const MAX_ATTEMPTS = 60; + const RETRY_DELAY_MS = 200; + let attempts = 0; + + const rootSwagger = document.getElementById('swagger-ui') || document.querySelector('.swagger-ui'); + + function attachToSection(opBlockSection) { + if (!opBlockSection || opBlockSection.dataset.expandCollapseAttached === 'true') + return; - // Iterate over each operation group - opBlockSections.forEach(opBlockSection => { const opBlockSectionHeader = opBlockSection.querySelector('h3'); - const expandOperationButton = opBlockSectionHeader.querySelector('button.expand-operation'); + const expandOperationButton = opBlockSectionHeader?.querySelector('button.expand-operation'); + + // Create expand or collapse button, if header exists + if (opBlockSectionHeader) { + // Avoid duplicate button + if (!opBlockSectionHeader.querySelector('.expand-collapse-all-btn')) { + const expandOrCollapseButton = document.createElement('button'); + expandOrCollapseButton.setAttribute('title', 'Expand/Collapse all the operations'); + expandOrCollapseButton.classList.add('expand-collapse-all-btn'); + expandOrCollapseButton.innerHTML = 'Expand/Collapse All'; + + // Insert before existing expand control if possible, otherwise append + if (expandOperationButton) { + expandOperationButton.before(expandOrCollapseButton); + } else { + opBlockSectionHeader.appendChild(expandOrCollapseButton); + } - // Create expand or collapse button, if needed - if (expandOperationButton) { - const expandOrCollapseButton = document.createElement('button'); - expandOrCollapseButton.setAttribute('title', 'Expand/Collapse all the operations'); - expandOrCollapseButton.classList.add('expand-collapse-all-btn'); - expandOrCollapseButton.innerHTML = 'Expand/Collapse All'; + expandOrCollapseButton.addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + + const opBlocks = opBlockSection.querySelectorAll('.opblock .opblock-control-arrow'); + if (!opBlocks || opBlocks.length === 0) { + // Nothing to do + return; + } + + const allExpanded = Array.from(opBlocks).every(opBlock => opBlock.getAttribute('aria-expanded') === 'true'); + + if (allExpanded) { + // Collapse all + opBlocks.forEach(opBlock => { + if (opBlock.getAttribute('aria-expanded') === 'true') { + opBlock.click(); + } + }); + } else { + // Expand all + opBlocks.forEach(opBlock => { + if (opBlock.getAttribute('aria-expanded') === 'false') { + opBlock.click(); + } + }); + } + }); + } + } - opBlockSectionHeader.insertBefore(expandOrCollapseButton, expandOperationButton); + // mark as processed to prevent duplicates + opBlockSection.dataset.expandCollapseAttached = 'true'; + } - expandOrCollapseButton.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); + function processAllSections() { + const opBlockSections = document.querySelectorAll('.opblock-tag-section'); + if (!opBlockSections || opBlockSections.length === 0) return false; - const opBlocks = opBlockSection.querySelectorAll('.opblock .opblock-control-arrow'); - const allExpanded = Array.from(opBlocks).every(opBlock => opBlock.getAttribute('aria-expanded') === 'true'); + opBlockSections.forEach(section => attachToSection(section)); + return true; + } - if (allExpanded) { - // Collapse all (click to collapse) - opBlocks.forEach(opBlock => { - if (opBlock.getAttribute('aria-expanded') === 'true') { - opBlock.click(); // Collapse - } - }); - } else { - // Expand all (click to expand) - opBlocks.forEach(opBlock => { - if (opBlock.getAttribute('aria-expanded') === 'false') { - opBlock.click(); // Expand + function waitAndProcess() { + attempts++; + const found = processAllSections(); + + if (found) { + // also observe for new sections added later (incremental/rendering) + try { + const containerToObserve = rootSwagger || document.body; + const observer = new MutationObserver(mutations => { + for (const m of mutations) { + if (m.addedNodes && m.addedNodes.length > 0) { + // try to attach to any new sections + processAllSections(); } - }); - } - }); + } + }); + + observer.observe(containerToObserve, { childList: true, subtree: true }); + } catch (e) { + // ignore observation errors + } + + return; } - }); + + if (attempts < MAX_ATTEMPTS) { + setTimeout(waitAndProcess, RETRY_DELAY_MS); + } else { + // Give up after multiple attempts + return; + } + } + + // Initial kick-off + waitAndProcess(); } function setUpThemeSwitcher(enabled) { @@ -172,7 +238,7 @@ function setUpThemeSwitcher(enabled) { return response.json(); }) .then(data => { - if (!data || !data.themes || data.themes.length < 2) { + if (!data?.themes || data.themes.length < 2) { console.warn('[ThemeSwitcher] Not enough themes available for switcher'); return; } @@ -186,35 +252,6 @@ function setUpThemeSwitcher(enabled) { console.error('[ThemeSwitcher] Error loading themes:', error); }); - function detectCurrentTheme(themes) { - // Check for data-theme attribute first (most reliable) - const activeLink = document.querySelector('link[rel="stylesheet"]:not([disabled])[data-theme]'); - if (activeLink && activeLink.dataset.theme) { - return activeLink.dataset.theme; - } - - // Fallback: check href patterns - const styleElements = document.querySelectorAll('link[rel="stylesheet"]:not([disabled])'); - - for (const element of styleElements) { - const theme = themes.find(t => { - if (element.href) { - return element.href.includes(t.cssPath) || - element.href.endsWith(t.cssPath) || - element.href.includes(t.name.toLowerCase()); - } - return false; - }); - - if (theme) { - return theme.name; - } - } - - // Fallback to first theme - return themes.length > 0 ? themes[0].name : null; - } - function restoreSavedTheme(themes) { const saved = localStorage.getItem(STORAGE_KEY); @@ -256,7 +293,7 @@ function setUpThemeSwitcher(enabled) { // Insert before pin button if it exists const pinButton = document.getElementById('pin-topbar-btn'); if (pinButton) { - topbarWrapper.insertBefore(select, pinButton); + pinButton.before(select); } else { topbarWrapper.appendChild(select); } @@ -278,12 +315,12 @@ function setUpThemeSwitcher(enabled) { allStyleLinks.forEach(link => { // Check if this is a theme stylesheet (has data-theme or matches a known theme path) const isThemeStylesheet = link.dataset.theme || - allThemePaths.some(path => link.href && link.href.endsWith(path)); + allThemePaths.some(path => link.href?.endsWith(path)); if (isThemeStylesheet) { // Use exact path matching to avoid substring issues const isTargetTheme = (link.dataset.theme === themeName) || - (link.href && link.href.endsWith(theme.cssPath)); + (link.href?.endsWith(theme.cssPath)); if (isTargetTheme) { link.disabled = false; @@ -310,15 +347,42 @@ function setUpThemeSwitcher(enabled) { dropdown.value = themeName; } } +} +function detectCurrentTheme(themes) { + // Check for data-theme attribute first (most reliable) + const activeLink = document.querySelector('link[rel="stylesheet"]:not([disabled])[data-theme]'); + if (activeLink?.dataset.theme) { + return activeLink.dataset.theme; + } + + // Fallback: check href patterns + const styleElements = document.querySelectorAll('link[rel="stylesheet"]:not([disabled])'); - function formatThemeName(name, format) { - const formatted = name - .replace(/([A-Z])/g, ' $1') - .trim() - .split(/[\s_-]+/) - .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) - .join(' '); + for (const element of styleElements) { + const theme = themes.find(t => { + if (element.href) { + return element.href.includes(t.cssPath) || + element.href.endsWith(t.cssPath) || + element.href.includes(t.name.toLowerCase()); + } + return false; + }); - return format.replace('{name}', formatted); + if (theme) { + return theme.name; + } } + + // Fallback to first theme + return themes.length > 0 ? themes[0].name : null; +} +function formatThemeName(name, format) { + const formatted = name + .replaceAll(/([A-Z])/g, ' $1') + .trim() + .split(/[\s_-]+/) + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(' '); + + return format.replace('{name}', formatted); } \ No newline at end of file diff --git a/src/AspNetCore.Swagger.Themes.Common/AspNetCore/Swagger/Themes/Scripts/ui.min.js b/src/AspNetCore.Swagger.Themes.Common/AspNetCore/Swagger/Themes/Scripts/ui.min.js index 1c3411a..fd477c8 100644 --- a/src/AspNetCore.Swagger.Themes.Common/AspNetCore/Swagger/Themes/Scripts/ui.min.js +++ b/src/AspNetCore.Swagger.Themes.Common/AspNetCore/Swagger/Themes/Scripts/ui.min.js @@ -2,4 +2,4 @@ `}function setUnpinnedIconTo(e){e.innerHTML=` - `}function setUpScrollToTopButton(e){if(!1===e)return;let t=document.createElement("div");t.classList.add("scroll-to-top-wrapper");let n=document.createElement("button");n.setAttribute("id","scroll-to-top-btn"),n.setAttribute("title","Back to top"),n.addEventListener("click",()=>{scrollToTop(),n?.blur()}),t.appendChild(n);let l=document.getElementById("swagger-ui");l.appendChild(t);let r=()=>{window.scrollY>=200?n.classList.add("showBtn"):n.classList.remove("showBtn")};window.addEventListener("scroll",r),window.addEventListener("resize",r)}function scrollToTop(){rootElement.scrollTo({top:0,behavior:"smooth"})}function setUpExpandAndCollapseOperationsButtons(e){if(!1===e)return;let t=document.querySelectorAll(".opblock-tag-section");t.forEach(e=>{let t=e.querySelector("h3"),n=t.querySelector("button.expand-operation");if(n){let l=document.createElement("button");l.setAttribute("title","Expand/Collapse all the operations"),l.classList.add("expand-collapse-all-btn"),l.innerHTML="Expand/Collapse All",t.insertBefore(l,n),l.addEventListener("click",t=>{t.preventDefault(),t.stopPropagation();let n=e.querySelectorAll(".opblock .opblock-control-arrow"),l=Array.from(n).every(e=>"true"===e.getAttribute("aria-expanded"));l?n.forEach(e=>{"true"===e.getAttribute("aria-expanded")&&e.click()}):n.forEach(e=>{"false"===e.getAttribute("aria-expanded")&&e.click()})})}})}function setUpThemeSwitcher(e){if(!1===e)return;let t="swaggerui-theme-preference",n=null,l=null;function r(e,r){let a=n?.themes.find(t=>t.name===e);if(!a){console.warn(`[ThemeSwitcher] Theme not found: ${e}`);return}let o=document.querySelectorAll('link[rel="stylesheet"]'),i=n.themes.map(e=>e.cssPath),s=!1;o.forEach(t=>{let n=t.dataset.theme||i.some(e=>t.href&&t.href.endsWith(e));if(n){let l=t.dataset.theme===e||t.href&&t.href.endsWith(a.cssPath);l?(t.disabled=!1,t.dataset.theme=e,s=!0):t.disabled=!0}}),s||console.warn(`[ThemeSwitcher] Could not activate theme: ${e}`),l=e,r&&localStorage.setItem(t,e);let $=document.getElementById("theme-switcher-select");$&&$.value!==e&&($.value=e)}fetch("/themes/metadata.json").then(e=>e.ok?e.json():(console.warn("[ThemeSwitcher] Failed to load theme metadata"),null)).then(e=>{if(!e||!e.themes||e.themes.length<2){console.warn("[ThemeSwitcher] Not enough themes available for switcher");return}n=e,l=function e(t){let n=document.querySelector('link[rel="stylesheet"]:not([disabled])[data-theme]');if(n&&n.dataset.theme)return n.dataset.theme;let l=document.querySelectorAll('link[rel="stylesheet"]:not([disabled])');for(let r of l){let a=t.find(e=>!!r.href&&(r.href.includes(e.cssPath)||r.href.endsWith(e.cssPath)||r.href.includes(e.name.toLowerCase())));if(a)return a.name}return t.length>0?t[0].name:null}(e.themes),function e(n){let a=localStorage.getItem(t);a&&n.some(e=>e.name===a)&&a!==l&&r(a,!1)}(e.themes),function e(t){let n=document.querySelector(".topbar-wrapper");if(!n){console.warn("[ThemeSwitcher] Topbar not found");return}let a=document.createElement("select");a.id="theme-switcher-select",a.setAttribute("aria-label","Select theme"),a.title="Switch theme",t.themes.forEach(e=>{let n=document.createElement("option");n.value=e.name,n.textContent=function e(t,n){let l=t.replace(/([A-Z])/g," $1").trim().split(/[\s_-]+/).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(" ");return n.replace("{name}",l)}(e.name,t.config?.displayFormat||"{name}"),e.name===l&&(n.selected=!0),a.appendChild(n)}),a.addEventListener("change",e=>{r(e.target.value,!0)});let o=document.getElementById("pin-topbar-btn");o?n.insertBefore(a,o):n.appendChild(a)}(e)}).catch(e=>{console.error("[ThemeSwitcher] Error loading themes:",e)})}window.onpageshow=function(){let e=setInterval(function(){null!=document.getElementById("swagger-ui")&&(clearInterval(e),console.log("Hello Swagger UI!"),setUpPinnableTopbar({$PINNABLE_TOPBAR}),setUpScrollToTopButton({$BACK_TO_TOP}),setUpExpandAndCollapseOperationsButtons({$EXPAND_COLLAPSE_ALL_OPERATIONS}),setUpThemeSwitcher({$THEME_SWITCHER}))},100)}; \ No newline at end of file + `}function setUpScrollToTopButton(e){if(!1===e)return;let t=document.createElement("div");t.classList.add("scroll-to-top-wrapper");let n=document.createElement("button");n.setAttribute("id","scroll-to-top-btn"),n.setAttribute("title","Back to top"),n.addEventListener("click",()=>{scrollToTop(),n?.blur()}),t.appendChild(n);let l=document.getElementById("swagger-ui");l.appendChild(t);let r=()=>{window.scrollY>=200?n.classList.add("showBtn"):n.classList.remove("showBtn")};window.addEventListener("scroll",r),window.addEventListener("resize",r)}function scrollToTop(){rootElement.scrollTo({top:0,behavior:"smooth"})}function setUpExpandAndCollapseOperationsButtons(e){if(!1===e)return;let t=0,n=document.getElementById("swagger-ui")||document.querySelector(".swagger-ui");function l(){let e=document.querySelectorAll(".opblock-tag-section");return!!e&&0!==e.length&&(e.forEach(e=>(function e(t){if(!t||"true"===t.dataset.expandCollapseAttached)return;let n=t.querySelector("h3"),l=n?.querySelector("button.expand-operation");if(n&&!n.querySelector(".expand-collapse-all-btn")){let r=document.createElement("button");r.setAttribute("title","Expand/Collapse all the operations"),r.classList.add("expand-collapse-all-btn"),r.innerHTML="Expand/Collapse All",l?l.before(r):n.appendChild(r),r.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation();let n=t.querySelectorAll(".opblock .opblock-control-arrow");if(!n||0===n.length)return;let l=Array.from(n).every(e=>"true"===e.getAttribute("aria-expanded"));l?n.forEach(e=>{"true"===e.getAttribute("aria-expanded")&&e.click()}):n.forEach(e=>{"false"===e.getAttribute("aria-expanded")&&e.click()})})}t.dataset.expandCollapseAttached="true"})(e)),!0)}!function e(){t++;let r=l();if(r){try{let a=n||document.body,o=new MutationObserver(e=>{for(let t of e)t.addedNodes&&t.addedNodes.length>0&&l()});o.observe(a,{childList:!0,subtree:!0})}catch(i){}return}t<60&&setTimeout(e,200)}()}function setUpThemeSwitcher(e){if(!1===e)return;let t="swaggerui-theme-preference",n=null,l=null;function r(e,r){let a=n?.themes.find(t=>t.name===e);if(!a){console.warn(`[ThemeSwitcher] Theme not found: ${e}`);return}let o=document.querySelectorAll('link[rel="stylesheet"]'),i=n.themes.map(e=>e.cssPath),s=!1;o.forEach(t=>{let n=t.dataset.theme||i.some(e=>t.href?.endsWith(e));if(n){let l=t.dataset.theme===e||t.href?.endsWith(a.cssPath);l?(t.disabled=!1,t.dataset.theme=e,s=!0):t.disabled=!0}}),s||console.warn(`[ThemeSwitcher] Could not activate theme: ${e}`),l=e,r&&localStorage.setItem(t,e);let $=document.getElementById("theme-switcher-select");$&&$.value!==e&&($.value=e)}fetch("/themes/metadata.json").then(e=>e.ok?e.json():(console.warn("[ThemeSwitcher] Failed to load theme metadata"),null)).then(e=>{if(!e?.themes||e.themes.length<2){console.warn("[ThemeSwitcher] Not enough themes available for switcher");return}n=e,l=detectCurrentTheme(e.themes),function e(n){let a=localStorage.getItem(t);a&&n.some(e=>e.name===a)&&a!==l&&r(a,!1)}(e.themes),function e(t){let n=document.querySelector(".topbar-wrapper");if(!n){console.warn("[ThemeSwitcher] Topbar not found");return}let a=document.createElement("select");a.id="theme-switcher-select",a.setAttribute("aria-label","Select theme"),a.title="Switch theme",t.themes.forEach(e=>{let n=document.createElement("option");n.value=e.name,n.textContent=formatThemeName(e.name,t.config?.displayFormat||"{name}"),e.name===l&&(n.selected=!0),a.appendChild(n)}),a.addEventListener("change",e=>{r(e.target.value,!0)});let o=document.getElementById("pin-topbar-btn");o?o.before(a):n.appendChild(a)}(e)}).catch(e=>{console.error("[ThemeSwitcher] Error loading themes:",e)})}function detectCurrentTheme(e){let t=document.querySelector('link[rel="stylesheet"]:not([disabled])[data-theme]');if(t?.dataset.theme)return t.dataset.theme;let n=document.querySelectorAll('link[rel="stylesheet"]:not([disabled])');for(let l of n){let r=e.find(e=>!!l.href&&(l.href.includes(e.cssPath)||l.href.endsWith(e.cssPath)||l.href.includes(e.name.toLowerCase())));if(r)return r.name}return e.length>0?e[0].name:null}function formatThemeName(e,t){let n=e.replaceAll(/([A-Z])/g," $1").trim().split(/[\s_-]+/).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(" ");return t.replace("{name}",n)}window.onpageshow=function(){let e=setInterval(function(){null!=document.getElementById("swagger-ui")&&(clearInterval(e),console.log("Hello Swagger UI!"),setUpPinnableTopbar({$PINNABLE_TOPBAR}),setUpScrollToTopButton({$BACK_TO_TOP}),setUpExpandAndCollapseOperationsButtons({$EXPAND_COLLAPSE_ALL_OPERATIONS}),setUpThemeSwitcher({$THEME_SWITCHER}))},100)}; \ No newline at end of file From 148d86e2cb46b969b764a84d8efad52e60fb9877 Mon Sep 17 00:00:00 2001 From: Teuz Date: Thu, 15 Jan 2026 19:42:11 +0100 Subject: [PATCH 2/4] Clean up example projects --- .../Program.cs | 12 +++++------ .../SwaggerThemes/CustomTheme.cs | 21 ------------------- .../Program.cs | 12 +++++------ .../SwaggerThemes/CustomTheme.cs | 21 ------------------- .../SwaggerThemes/custom.css | 8 ------- 5 files changed, 12 insertions(+), 62 deletions(-) delete mode 100644 samples/Sample.AspNetCore.SwaggerUI.NSwag/SwaggerThemes/CustomTheme.cs delete mode 100644 samples/Sample.AspNetCore.SwaggerUI.Swashbuckle/SwaggerThemes/CustomTheme.cs delete mode 100644 samples/Sample.AspNetCore.SwaggerUI.Swashbuckle/SwaggerThemes/custom.css diff --git a/samples/Sample.AspNetCore.SwaggerUI.NSwag/Program.cs b/samples/Sample.AspNetCore.SwaggerUI.NSwag/Program.cs index e631f48..503ef0b 100644 --- a/samples/Sample.AspNetCore.SwaggerUI.NSwag/Program.cs +++ b/samples/Sample.AspNetCore.SwaggerUI.NSwag/Program.cs @@ -18,7 +18,7 @@ // ======================================== // 1. Simple predefined theme (no theme switcher) - app.UseSwaggerUi(Theme.Dark, c => c.DocumentTitle = "Sample API - Dark Theme"); + //app.UseSwaggerUi(Theme.Dark, c => c.DocumentTitle = "Sample API - Dark Theme"); // 2. Inline CSS theme //app.UseSwaggerUi("body { background-color: #1a1a2e; color: #eee; }", c => @@ -126,11 +126,11 @@ // ======================================== // 13. Enable all advanced UI features - //app.UseSwaggerUi(Theme.Dark, c => - //{ - // c.DocumentTitle = "Sample API - All Features"; - // c.EnableAllAdvancedOptions(); // Pinnable topbar, back-to-top, sticky ops, expand/collapse, theme switcher - //}); + app.UseSwaggerUi(Theme.Dark, c => + { + c.DocumentTitle = "Sample API - All Features"; + c.EnableAllAdvancedOptions(); + }); // 14. Individual advanced features //app.UseSwaggerUi(Theme.Light, c => diff --git a/samples/Sample.AspNetCore.SwaggerUI.NSwag/SwaggerThemes/CustomTheme.cs b/samples/Sample.AspNetCore.SwaggerUI.NSwag/SwaggerThemes/CustomTheme.cs deleted file mode 100644 index 8c992ab..0000000 --- a/samples/Sample.AspNetCore.SwaggerUI.NSwag/SwaggerThemes/CustomTheme.cs +++ /dev/null @@ -1,21 +0,0 @@ -using AspNetCore.Swagger.Themes; - -namespace SwaggerThemes; - -public class CustomTheme : Theme -{ - protected CustomTheme(string fileName) : base(fileName) - { - } - - public static CustomTheme Custom => new("custom.css"); -} - -public class CustomMinifiedStyle : Theme -{ - protected CustomMinifiedStyle(string fileName, bool useMinified) : base(fileName, useMinified) - { - } - - public static CustomMinifiedStyle CustomMin => new("minifiedCustom.css", true); -} \ No newline at end of file diff --git a/samples/Sample.AspNetCore.SwaggerUI.Swashbuckle/Program.cs b/samples/Sample.AspNetCore.SwaggerUI.Swashbuckle/Program.cs index 037bf54..e9974d9 100644 --- a/samples/Sample.AspNetCore.SwaggerUI.Swashbuckle/Program.cs +++ b/samples/Sample.AspNetCore.SwaggerUI.Swashbuckle/Program.cs @@ -18,7 +18,7 @@ // ======================================== // 1. Simple predefined theme (no theme switcher) - app.UseSwaggerUI(Theme.Dark, c => c.DocumentTitle = "Sample API - Dark Theme"); + //app.UseSwaggerUI(Theme.Dark, c => c.DocumentTitle = "Sample API - Dark Theme"); // 2. Inline CSS theme //app.UseSwaggerUI("body { background-color: #1a1a2e; color: #eee; }", c => @@ -126,11 +126,11 @@ // ======================================== // 13. Enable all advanced UI features - //app.UseSwaggerUI(Theme.Dark, c => - //{ - // c.DocumentTitle = "Sample API - All Features"; - // c.EnableAllAdvancedOptions(); // Pinnable topbar, back-to-top, sticky ops, expand/collapse, theme switcher - //}); + app.UseSwaggerUI(Theme.Dark, c => + { + c.DocumentTitle = "Sample API - All Features"; + c.EnableAllAdvancedOptions(); + }); // 14. Individual advanced features //app.UseSwaggerUI(Theme.Light, c => diff --git a/samples/Sample.AspNetCore.SwaggerUI.Swashbuckle/SwaggerThemes/CustomTheme.cs b/samples/Sample.AspNetCore.SwaggerUI.Swashbuckle/SwaggerThemes/CustomTheme.cs deleted file mode 100644 index 8c992ab..0000000 --- a/samples/Sample.AspNetCore.SwaggerUI.Swashbuckle/SwaggerThemes/CustomTheme.cs +++ /dev/null @@ -1,21 +0,0 @@ -using AspNetCore.Swagger.Themes; - -namespace SwaggerThemes; - -public class CustomTheme : Theme -{ - protected CustomTheme(string fileName) : base(fileName) - { - } - - public static CustomTheme Custom => new("custom.css"); -} - -public class CustomMinifiedStyle : Theme -{ - protected CustomMinifiedStyle(string fileName, bool useMinified) : base(fileName, useMinified) - { - } - - public static CustomMinifiedStyle CustomMin => new("minifiedCustom.css", true); -} \ No newline at end of file diff --git a/samples/Sample.AspNetCore.SwaggerUI.Swashbuckle/SwaggerThemes/custom.css b/samples/Sample.AspNetCore.SwaggerUI.Swashbuckle/SwaggerThemes/custom.css deleted file mode 100644 index 64bbe07..0000000 --- a/samples/Sample.AspNetCore.SwaggerUI.Swashbuckle/SwaggerThemes/custom.css +++ /dev/null @@ -1,8 +0,0 @@ -/* - Custom Style - - https://github.com/teociaps/SwaggerUI.Themes -*/ -body { - background-color: #4cff00; -} \ No newline at end of file From 6f08e5c437769cb92303c77a4ee6f95cd12990b0 Mon Sep 17 00:00:00 2001 From: Teuz Date: Thu, 15 Jan 2026 19:44:08 +0100 Subject: [PATCH 3/4] Fix docs and version misalignment --- build/Versioning.props | 2 +- src/AspNetCore.SwaggerUI.Themes/package-readme.md | 8 ++------ src/NSwag.AspNetCore.Themes/package-readme.md | 2 -- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/build/Versioning.props b/build/Versioning.props index 3e67247..e09c83a 100644 --- a/build/Versioning.props +++ b/build/Versioning.props @@ -12,7 +12,7 @@ $(Today)-$(Floored) 3 0 - 0 + 1 -$(Revision) $(MajorVersion).$(MinorVersion).$(PatchVersion)$(VersionSuffix) diff --git a/src/AspNetCore.SwaggerUI.Themes/package-readme.md b/src/AspNetCore.SwaggerUI.Themes/package-readme.md index c4d6109..6d78629 100644 --- a/src/AspNetCore.SwaggerUI.Themes/package-readme.md +++ b/src/AspNetCore.SwaggerUI.Themes/package-readme.md @@ -6,10 +6,8 @@ **[Get Started](https://github.com/teociaps/SwaggerUI.Themes/wiki/Getting-Started)** • **[View Built-in Themes](https://github.com/teociaps/SwaggerUI.Themes/wiki/Predefined-Themes)** • **[Full Documentation](https://github.com/teociaps/SwaggerUI.Themes/wiki)** - - -> ⚠️ **Version 3.0 Breaking Changes** -> Upgrading from v2.0.0? Please review the **[Migration Guide](https://github.com/teociaps/SwaggerUI.Themes/wiki/Migration-v3)** for important API changes. +> ⚠️ **Version 3.x Breaking Changes** +> Upgrading from v2.x? Please review the **[Migration Guide](https://github.com/teociaps/SwaggerUI.Themes/wiki/Migration-v3)** for important API changes. ## 🚀 Quick Start @@ -17,8 +15,6 @@ dotnet add package AspNetCore.SwaggerUI.Themes ``` -## Quick Start - ```csharp // Apply a theme app.UseSwaggerUI(Theme.Dark); diff --git a/src/NSwag.AspNetCore.Themes/package-readme.md b/src/NSwag.AspNetCore.Themes/package-readme.md index 06369c5..c59e707 100644 --- a/src/NSwag.AspNetCore.Themes/package-readme.md +++ b/src/NSwag.AspNetCore.Themes/package-readme.md @@ -15,8 +15,6 @@ dotnet add package NSwag.AspNetCore.Themes ``` -## Quick Start - ```csharp // Apply a theme app.UseSwaggerUi(Theme.Dark); From d671d880bd63f6019e9bb91838fca0023b97d568 Mon Sep 17 00:00:00 2001 From: Teuz Date: Tue, 3 Feb 2026 19:59:09 +0100 Subject: [PATCH 4/4] Bump version to 3.0.2 --- build/Versioning.props | 2 +- .../AspNetCore.SwaggerUI.Themes.csproj | 2 +- src/NSwag.AspNetCore.Themes/NSwag.AspNetCore.Themes.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/Versioning.props b/build/Versioning.props index e09c83a..d078017 100644 --- a/build/Versioning.props +++ b/build/Versioning.props @@ -12,7 +12,7 @@ $(Today)-$(Floored) 3 0 - 1 + 2 -$(Revision) $(MajorVersion).$(MinorVersion).$(PatchVersion)$(VersionSuffix) diff --git a/src/AspNetCore.SwaggerUI.Themes/AspNetCore.SwaggerUI.Themes.csproj b/src/AspNetCore.SwaggerUI.Themes/AspNetCore.SwaggerUI.Themes.csproj index 7631c4b..c297dd7 100644 --- a/src/AspNetCore.SwaggerUI.Themes/AspNetCore.SwaggerUI.Themes.csproj +++ b/src/AspNetCore.SwaggerUI.Themes/AspNetCore.SwaggerUI.Themes.csproj @@ -7,7 +7,7 @@ 3 0 - 1 + 2 $(MajorVersion).$(MinorVersion).$(PatchVersion)$(VersionSuffix) diff --git a/src/NSwag.AspNetCore.Themes/NSwag.AspNetCore.Themes.csproj b/src/NSwag.AspNetCore.Themes/NSwag.AspNetCore.Themes.csproj index 30c919a..19caafd 100644 --- a/src/NSwag.AspNetCore.Themes/NSwag.AspNetCore.Themes.csproj +++ b/src/NSwag.AspNetCore.Themes/NSwag.AspNetCore.Themes.csproj @@ -7,7 +7,7 @@ 3 0 - 1 + 2 $(MajorVersion).$(MinorVersion).$(PatchVersion)$(VersionSuffix)