diff --git a/build/Versioning.props b/build/Versioning.props
index 3e67247..d078017 100644
--- a/build/Versioning.props
+++ b/build/Versioning.props
@@ -12,7 +12,7 @@
$(Today)-$(Floored)
3
0
- 0
+ 2
-$(Revision)
$(MajorVersion).$(MinorVersion).$(PatchVersion)$(VersionSuffix)
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
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
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/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/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)
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);