Mkdocs search fixers
This commit is contained in:
@@ -7,12 +7,24 @@ if [ "$NODE_ENV" = "production" ] && [ "$NODE_TLS_REJECT_UNAUTHORIZED" = "0" ];
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wait for PostgreSQL to be ready before running migrations
|
||||
echo "Waiting for database..."
|
||||
MAX_WAIT=30
|
||||
WAITED=0
|
||||
until echo "SELECT 1" | npx prisma db execute --stdin --schema ./prisma/schema.prisma 2>/dev/null; do
|
||||
sleep 2
|
||||
WAITED=$((WAITED + 2))
|
||||
if [ $WAITED -ge $MAX_WAIT ]; then
|
||||
echo "FATAL: Database not available after ${MAX_WAIT}s"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "Database ready (${WAITED}s)"
|
||||
|
||||
# Run migrations — fail hard on error (never fall back to db push, which causes drift)
|
||||
echo "Running Prisma migrations..."
|
||||
npx prisma migrate deploy 2>&1 || {
|
||||
echo "Migration failed, falling back to schema push..."
|
||||
npx prisma db push --skip-generate 2>&1
|
||||
}
|
||||
echo "Database sync complete."
|
||||
npx prisma migrate deploy 2>&1
|
||||
echo "Migrations complete."
|
||||
|
||||
echo "Running database seed..."
|
||||
npx prisma db seed 2>&1
|
||||
|
||||
@@ -589,6 +589,12 @@ class HeaderBuilderService {
|
||||
<div class="cm-header-nav__links">
|
||||
<div class="cm-header-nav__links-inner">
|
||||
${desktopLinks}
|
||||
<label for="__search" class="cm-header-nav__utility" title="Search">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
</button>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span class="cm-header-nav__label">Sign In</span>
|
||||
@@ -621,6 +627,20 @@ class HeaderBuilderService {
|
||||
</div>
|
||||
<div class="cm-header-nav__mobile-links">
|
||||
${mobileLinks}
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
|
||||
<span class="material-icons-outlined">search</span>
|
||||
<span>Search</span>
|
||||
</label>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
|
||||
<span class="material-icons-outlined">dark_mode</span>
|
||||
<span>Dark Mode</span>
|
||||
</button>
|
||||
<button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
|
||||
<span class="material-icons-outlined">menu_book</span>
|
||||
<span>Docs Navigation</span>
|
||||
</button>
|
||||
<div class="cm-header-nav__mobile-divider"></div>
|
||||
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
|
||||
<span class="material-icons-outlined">login</span>
|
||||
<span>Sign In</span>
|
||||
@@ -724,6 +744,74 @@ class HeaderBuilderService {
|
||||
}
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
// Palette toggle (dark/light mode)
|
||||
function togglePalette() {
|
||||
var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (!inputs[i].checked) { inputs[i].click(); break; }
|
||||
}
|
||||
setTimeout(updatePaletteIcon, 50);
|
||||
}
|
||||
function updatePaletteIcon() {
|
||||
var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
|
||||
var isDark = scheme === 'slate';
|
||||
var icon = isDark ? 'light_mode' : 'dark_mode';
|
||||
document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
|
||||
el.textContent = icon;
|
||||
});
|
||||
var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
|
||||
if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
var ptBtn = document.getElementById('cm-palette-toggle');
|
||||
var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
|
||||
if (ptBtn) ptBtn.addEventListener('click', togglePalette);
|
||||
if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
|
||||
// Docs sidebar toggle (opens Material's docs navigation drawer)
|
||||
var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
|
||||
if (docsSidebarBtn) {
|
||||
docsSidebarBtn.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
var dt = document.getElementById('__drawer');
|
||||
if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
|
||||
});
|
||||
}
|
||||
// Close custom drawer when search label is clicked on mobile + auto-focus input
|
||||
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
closeDrawer();
|
||||
setTimeout(function() {
|
||||
var input = document.querySelector('.md-search__input');
|
||||
if (input) input.focus();
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
// Search activation: mirror checkbox state as a body class for CSS targeting.
|
||||
// This avoids reliance on the ~ sibling combinator (fragile with template blocks).
|
||||
var searchToggle = document.getElementById('__search');
|
||||
if (searchToggle) {
|
||||
function syncSearchClass() {
|
||||
document.body.classList.toggle('cm-search-active', searchToggle.checked);
|
||||
}
|
||||
searchToggle.addEventListener('change', syncSearchClass);
|
||||
syncSearchClass();
|
||||
// Click-outside to dismiss search
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchToggle.checked) return;
|
||||
var panel = document.querySelector('.md-search__inner');
|
||||
if (panel && panel.contains(e.target)) return;
|
||||
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
|
||||
searchToggle.checked = false;
|
||||
syncSearchClass();
|
||||
});
|
||||
// Also sync on Escape key (Material toggles checkbox via JS)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
|
||||
});
|
||||
}
|
||||
// Init palette icon + observe changes
|
||||
setTimeout(updatePaletteIcon, 100);
|
||||
new MutationObserver(function() { updatePaletteIcon(); })
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
@@ -966,8 +1054,127 @@ class HeaderBuilderService {
|
||||
.cm-header-nav__hamburger { display: block; }
|
||||
.cm-header-nav__dropdown-menu { display: none !important; }
|
||||
}
|
||||
/* Hidden Material header — stays at 0 height normally */
|
||||
.md-header--cm-hidden {
|
||||
height: 0 !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* === DESKTOP SEARCH (>= 60em / 960px) === */
|
||||
@media screen and (min-width: 60em) {
|
||||
/* When search is active, make the search panel a fixed dropdown below custom header */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 56px !important;
|
||||
right: 16px !important;
|
||||
left: auto !important;
|
||||
width: min(34rem, calc(100vw - 32px)) !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
border-radius: 0 0 8px 8px !important;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
|
||||
z-index: 300 !important;
|
||||
}
|
||||
|
||||
/* Dark overlay behind search panel */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
background: rgba(0,0,0,0.54) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: 299 !important;
|
||||
border-radius: 0 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* === MOBILE SEARCH (< 60em / 960px) === */
|
||||
@media screen and (max-width: 59.984375em) {
|
||||
/* Full-screen search takeover on mobile */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__inner {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
transform: none !important;
|
||||
z-index: 300 !important;
|
||||
background: var(--md-default-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force search results to show when active (both breakpoints) */
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__output {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
|
||||
max-height: 75vh !important;
|
||||
}
|
||||
.cm-palette-container {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Hide Material tabs — custom header covers navigation */
|
||||
.md-tabs { display: none !important; }
|
||||
/* Utility icon styling */
|
||||
.cm-header-nav__utility {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.cm-header-nav__utility:hover { color: #fff; }
|
||||
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
|
||||
.cm-header-nav__utility-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.85);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.cm-header-nav__mobile-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 24px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<header class="md-header md-header--cm-hidden" data-md-component="header">
|
||||
<div class="cm-palette-container">
|
||||
{% if config.theme.palette %}
|
||||
{% if not config.theme.palette is mapping %}
|
||||
{% include "partials/palette.html" %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if "material/search" in config.plugins %}
|
||||
{% include "partials/search.html" %}
|
||||
{% endif %}
|
||||
</header>
|
||||
{% endblock %}
|
||||
|
||||
{% block tabs %}{% endblock %}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user