diff --git a/apps/server/AliasVault.Client/AliasVault.Client.csproj b/apps/server/AliasVault.Client/AliasVault.Client.csproj
index cce2c1b8a..e390e131f 100644
--- a/apps/server/AliasVault.Client/AliasVault.Client.csproj
+++ b/apps/server/AliasVault.Client/AliasVault.Client.csproj
@@ -35,14 +35,83 @@
+
();
+ var styleBlocks = new System.Collections.Generic.List();
+
+ content = System.Text.RegularExpressions.Regex.Replace(content, @"()",
+ m => { scriptBlocks.Add(m.Groups[2].Value); return m.Groups[1].Value + "___SCRIPT_" + (scriptBlocks.Count - 1) + "___" + m.Groups[3].Value; },
+ System.Text.RegularExpressions.RegexOptions.Singleline);
+
+ content = System.Text.RegularExpressions.Regex.Replace(content, @"()",
+ m => { styleBlocks.Add(m.Groups[2].Value); return m.Groups[1].Value + "___STYLE_" + (styleBlocks.Count - 1) + "___" + m.Groups[3].Value; },
+ System.Text.RegularExpressions.RegexOptions.Singleline);
+
+ // Extract and preserve the header comment (contains ASCII art and license)
+ var headerMatch = System.Text.RegularExpressions.Regex.Match(content, @"()");
+ string headerComment = headerMatch.Success ? headerMatch.Groups[1].Value : "";
+ if (headerMatch.Success)
+ {
+ content = content.Replace(headerComment, "___HEADER_COMMENT___");
+ }
+
+ // Remove all other HTML comments
+ content = System.Text.RegularExpressions.Regex.Replace(content, @"", "", System.Text.RegularExpressions.RegexOptions.Singleline);
+
+ // Collapse whitespace between tags
+ content = System.Text.RegularExpressions.Regex.Replace(content, @">\s+<", "><");
+
+ // Remove leading/trailing whitespace from lines and collapse newlines
+ content = System.Text.RegularExpressions.Regex.Replace(content, @"^\s+", "", System.Text.RegularExpressions.RegexOptions.Multiline);
+ content = System.Text.RegularExpressions.Regex.Replace(content, @"\s+$", "", System.Text.RegularExpressions.RegexOptions.Multiline);
+ content = System.Text.RegularExpressions.Regex.Replace(content, @"\r?\n", "");
+ content = System.Text.RegularExpressions.Regex.Replace(content, @" +", " ");
+
+ // Restore script blocks (minify each one)
+ for (int i = 0; i < scriptBlocks.Count; i++)
+ {
+ var script = scriptBlocks[i];
+ // Remove JS single-line comments (but not URLs with //)
+ script = System.Text.RegularExpressions.Regex.Replace(script, @"(?+~])\s*", "$1");
+ style = style.Trim();
+ content = content.Replace("___STYLE_" + i + "___", style);
+ }
+
+ // Restore header comment at the end (preserving its formatting)
+ if (headerMatch.Success)
+ {
+ content = content.Replace("___HEADER_COMMENT___", "\n" + headerComment + "\n");
+ }
+ }
+
File.WriteAllText(OutputFile, content);
- Log.LogMessage(MessageImportance.High, "Replaced content in " + OutputFile);
+ Log.LogMessage(MessageImportance.High, (Minify ? "Minified and replaced" : "Replaced") + " content in " + OutputFile);
]]>
@@ -57,7 +126,7 @@
-
+
diff --git a/apps/server/AliasVault.Client/wwwroot/index.template.html b/apps/server/AliasVault.Client/wwwroot/index.template.html
index a30c3c13e..3011815cb 100644
--- a/apps/server/AliasVault.Client/wwwroot/index.template.html
+++ b/apps/server/AliasVault.Client/wwwroot/index.template.html
@@ -1,26 +1,19 @@
-
+
AliasVault
@@ -157,7 +150,6 @@
if (response.ok) {
translations = await response.json();
} else {
- // Fallback to English if the language file is not found
const fallbackResponse = await fetch('/locales/en.json');
translations = await fallbackResponse.json();
}
@@ -200,7 +192,6 @@
}
function localizeContent() {
- // Localize loading screen
const localizations = [
{ id: 'loading-title', key: 'loading.title' },
{ id: 'loading-message', key: 'loading.message' },
@@ -217,7 +208,6 @@
}
});
- // Set random security quote
const securityQuotes = getTranslation('quotes.security');
if (securityQuotes && securityQuotes.length > 0) {
const quoteElement = document.getElementById('security-quote');
@@ -245,7 +235,7 @@
const minDisplayTime = 1000;
const checkInterval = 500;
const refreshButtonTimeout = 300000;
- const scriptsCheckTimeout = 5000; // Check for blocked scripts after 5 seconds
+ const scriptsCheckTimeout = 5000;
const appElement = document.getElementById('app');
const refreshButton = document.getElementById('refresh-button');
@@ -294,8 +284,6 @@
if (progressText) progressText.classList.add('hidden');
};
- // Detect Blazor boot failures (e.g., after .NET version upgrades)
- // This handles transitions between major .NET versions where boot format changes
let bootFailureHandled = false;
async function handleBlazorBootFailure() {
if (bootFailureHandled) return;
@@ -304,20 +292,14 @@
console.info('AliasVault: Blazor boot failure detected. Refreshing cached files...');
try {
- // Clear service worker caches
if ('caches' in window) {
const cacheKeys = await caches.keys();
await Promise.all(cacheKeys.map(key => caches.delete(key)));
}
-
- // Unregister service workers
if ('serviceWorker' in navigator) {
const registrations = await navigator.serviceWorker.getRegistrations();
await Promise.all(registrations.map(r => r.unregister()));
}
-
- // Force browser to fetch fresh versions of critical files
- // cache: 'reload' bypasses HTTP cache and updates it with fresh content
const criticalFiles = [
'/',
'/_framework/blazor.webassembly.js',
@@ -329,8 +311,6 @@
} catch (e) {
console.error('AliasVault: Cache refresh failed:', e);
}
-
- // Navigate to cache-busted URL to force complete reload with fresh resources
const url = new URL(window.location.href);
url.searchParams.set('_r', Date.now().toString());
window.location.replace(url.toString());
@@ -347,7 +327,6 @@
if (errorMessage.includes('WebAssembly')) {
showError(getTranslation('errors.webAssemblyError'));
}
- // Detect Blazor boot failures and runtime errors that require a reload
else if (
errorMessage.includes('blazor.boot.json') ||
errorMessage.includes('Failed to load config file') ||
@@ -387,5 +366,4 @@
})
-