Compare commits

...
Author SHA1 Message Date
EthanHealy01 e5c28b82ea Merge branch 'main' into UIUX/RemoveLegacyLogo 2026-08-19 12:13:46 +01:00
EthanHealy01 a466543860 Merge branch 'main' into UIUX/RemoveLegacyLogo 2026-08-18 13:45:15 +01:00
EthanHealy01 1c649e5aa3 Merge main into UIUX/RemoveLegacyLogo: take main's branding structure, re-remove legacy logo
Resolved by accepting main's restructured brand assets (src/core/assets/brand,
modern-logo public path, Logo/BrandMark components, #7485 modern default),
then re-applying the legacy-logo removal on top of the new structure:
- delete classic-logo assets and vite copy target
- remove ui.logoStyle setting (backend property, template, config endpoint, admin UI)
- remove logoVariant preference and variant-resolution hooks
- drop manifest-classic.json and classic-logo static handlers/routes
- remove dead classic logo CSS
2026-08-17 20:48:38 +01:00
EthanHealy01 e9f0b82f05 Merge branch 'main' into UIUX/RemoveLegacyLogo 2026-06-17 18:32:14 +01:00
EthanHealy01 bdd20e7606 Merge branch 'main' into UIUX/RemoveLegacyLogo 2026-06-17 16:54:46 +01:00
EthanHealy01 87d493b84a fix(desktop): use modern logo for Tauri app/installer icons
Regenerate the full src-tauri icon set (icns, ico, PNG sizes, Windows
Square/Store tiles) from the modern logo (logo512) so the desktop app and
macOS DMG no longer show the legacy 'S' icon.
2026-06-17 15:32:21 +01:00
EthanHealy01 f7ea620952 Merge branch 'main' into UIUX/RemoveLegacyLogo 2026-06-17 15:05:55 +01:00
EthanHealy01 5591237381 Merge branch 'main' into UIUX/RemoveLegacyLogo 2026-06-16 19:00:32 +01:00
EthanHealy01 6df59833a9 Merge branch 'main' into UIUX/RemoveLegacyLogo 2026-06-16 17:57:46 +01:00
EthanHealy01 0f604c20d2 remove legacy logo from the app 2026-06-16 17:08:55 +01:00
35 changed files with 34 additions and 426 deletions
-2
View File
@@ -59,11 +59,9 @@ app/core/src/main/resources/static/*.html
app/core/src/main/resources/static/settings/
app/core/src/main/resources/static/locales/
app/core/src/main/resources/static/Login/
app/core/src/main/resources/static/classic-logo/
app/core/src/main/resources/static/modern-logo/
app/core/src/main/resources/static/og_images/
app/core/src/main/resources/static/samples/
app/core/src/main/resources/static/manifest-classic.json
app/core/src/main/resources/static/og-metadata.json
app/core/src/main/resources/static/og-metadata.saas.json
app/core/src/main/resources/static/sw-folder-retry.js
@@ -1316,7 +1316,6 @@ public class ApplicationProperties {
public static class Ui {
private String appNameNavbar;
private List<String> languages;
private String logoStyle = "modern"; // Options: "modern" (default) or "classic"
private boolean defaultHideUnavailableTools = false;
private boolean defaultHideUnavailableConversions = false;
private HideDisabledTools hideDisabledTools = new HideDisabledTools();
@@ -1325,14 +1324,6 @@ public class ApplicationProperties {
return appNameNavbar != null && !appNameNavbar.trim().isEmpty() ? appNameNavbar : null;
}
public String getLogoStyle() {
// Validate and return either "modern" or "classic"
if ("classic".equalsIgnoreCase(logoStyle)) {
return "classic";
}
return "modern"; // default
}
@Data
public static class HideDisabledTools {
private boolean googleDrive = false;
@@ -35,7 +35,6 @@ public class RequestUriUtils {
|| normalizedUri.startsWith("/locales/")
|| normalizedUri.startsWith("/Login/")
|| normalizedUri.startsWith("/samples/")
|| normalizedUri.startsWith("/classic-logo/")
|| normalizedUri.startsWith("/modern-logo/")
|| normalizedUri.startsWith("/og_images/")) {
return true;
@@ -46,7 +45,6 @@ public class RequestUriUtils {
|| "/favicon.ico".equals(normalizedUri)
|| "/manifest.json".equals(normalizedUri)
|| "/site.webmanifest".equals(normalizedUri)
|| "/manifest-classic.json".equals(normalizedUri)
|| "/index.html".equals(normalizedUri)) {
return true;
}
-4
View File
@@ -196,12 +196,8 @@ def generatedFrontendPaths = [
'site.webmanifest.br',
'browserconfig.xml.gz',
'browserconfig.xml.br',
'manifest-classic.json',
'manifest-classic.json.gz',
'manifest-classic.json.br',
'locales',
'Login',
'classic-logo',
'modern-logo',
'og_images',
'samples',
@@ -84,7 +84,6 @@ public class WebMvcConfig implements WebMvcConfigurer {
"/safari-pinned-tab.svg",
"/icons/**",
"/modern-logo/**",
"/classic-logo/**",
"/3rdPartyLicenses.json",
"/pdfjs/**",
"/pdfjs-legacy/**",
@@ -95,8 +94,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
"/vendor/**",
"/samples/**",
"/og_images/**",
"/Login/**",
"/manifest-classic.json")
"/Login/**")
.addResourceLocations(
staticPath,
"classpath:/static/",
@@ -123,9 +121,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
staticPath + "icons/",
"classpath:/static/icons/",
staticPath + "modern-logo/",
"classpath:/static/modern-logo/",
staticPath + "classic-logo/",
"classpath:/static/classic-logo/")
"classpath:/static/modern-logo/")
.setCacheControl(
CacheControl.maxAge(Duration.ofDays(1))
.cachePublic()
@@ -216,7 +216,6 @@ public class ConfigController {
// Extract values from ApplicationProperties
configData.put("appNameNavbar", applicationProperties.getUi().getAppNameNavbar());
configData.put("languages", applicationProperties.getUi().getLanguages());
configData.put("logoStyle", applicationProperties.getUi().getLogoStyle());
configData.put("defaultLocale", applicationProperties.getSystem().getDefaultLocale());
// User preference defaults
@@ -59,7 +59,6 @@ public class ReactRoutingController {
"assets",
"locales",
"modern-logo",
"classic-logo",
"Login",
"og_images",
"samples");
@@ -295,13 +294,13 @@ public class ReactRoutingController {
// (Real storage endpoints live under /api/v1/storage/files, already
// excluded by the leading `api` token in the same regex.)
@GetMapping(
"/{path:^(?!api|static|robots\\.txt|favicon\\.ico|manifest.*\\.json|pipeline|pdfjs|pdfjs-legacy|pdfium|vendor|fonts|images|css|js|assets|locales|modern-logo|classic-logo|Login|og_images|samples)[^\\.]*$}")
"/{path:^(?!api|static|robots\\.txt|favicon\\.ico|manifest.*\\.json|pipeline|pdfjs|pdfjs-legacy|pdfium|vendor|fonts|images|css|js|assets|locales|modern-logo|Login|og_images|samples)[^\\.]*$}")
public ResponseEntity<String> forwardRootPaths(HttpServletRequest request) throws IOException {
return serveIndexHtml(request);
}
@GetMapping(
"/{path:^(?!api|static|pipeline|pdfjs|pdfjs-legacy|pdfium|vendor|fonts|images|css|js|assets|locales|modern-logo|classic-logo|Login|og_images|samples)[^\\.]*}/{subpath:^(?!.*\\.).*$}")
"/{path:^(?!api|static|pipeline|pdfjs|pdfjs-legacy|pdfium|vendor|fonts|images|css|js|assets|locales|modern-logo|Login|og_images|samples)[^\\.]*}/{subpath:^(?!.*\\.).*$}")
public ResponseEntity<String> forwardNestedPaths(HttpServletRequest request)
throws IOException {
return serveIndexHtml(request);
@@ -313,7 +313,6 @@ autoPipeline:
ui:
appNameNavbar: "" # custom app/brand name. NOTE: no longer shown in the navbar (the navbar renders the logo). It IS used as the browser tab title and as the TOTP/2FA issuer label in authenticator apps. Empty falls back to "Stirling PDF"
logoStyle: modern # Options: 'modern' (default - minimalist logo) or 'classic' (legacy S icon)
languages: [] # If empty, all languages are enabled. To restrict to specific languages, use a whitelist like ["de_DE", "pl_PL", "sv_SE"]. Empty list or not restricting any languages will enable all available languages.
defaultHideUnavailableTools: false # Default user preference: hide disabled tools instead of greying them out
defaultHideUnavailableConversions: false # Default user preference: hide disabled conversion options instead of greying them out
@@ -108,14 +108,14 @@ class AdminSettingsControllerTest {
@Test
@DisplayName("merges pending changes when includePending is true")
void mergesPendingChanges() {
putPending("ui.logoStyle", "modern");
putPending("ui.appNameNavbar", "Custom Name");
ResponseEntity<?> response = controller.getSettings(true);
assertThat(response.getStatusCode().is2xxSuccessful()).isTrue();
Map<?, ?> body = (Map<?, ?>) response.getBody();
Map<?, ?> ui = (Map<?, ?>) body.get("ui");
assertThat(ui.get("logoStyle")).isEqualTo("modern");
assertThat(ui.get("appNameNavbar")).isEqualTo("Custom Name");
}
@Test
@@ -490,14 +490,14 @@ class AdminSettingsControllerTest {
@Test
@DisplayName("returns value for an existing key")
void existingKey() {
applicationProperties.getUi().setLogoStyle("modern");
applicationProperties.getUi().setAppNameNavbar("Custom Name");
ResponseEntity<?> response = controller.getSettingValue("ui.logoStyle");
ResponseEntity<?> response = controller.getSettingValue("ui.appNameNavbar");
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
SettingValueResponse body = (SettingValueResponse) response.getBody();
assertThat(body.getKey()).isEqualTo("ui.logoStyle");
assertThat(body.getValue()).isEqualTo("modern");
assertThat(body.getKey()).isEqualTo("ui.appNameNavbar");
assertThat(body.getValue()).isEqualTo("Custom Name");
}
@Test
@@ -1217,14 +1217,6 @@ description = "Languages that users can select from (leave empty to enable all l
label = "Available Languages"
placeholder = "Select languages"
[admin.settings.general.logoStyle]
classic = "Classic"
classicAlt = "Classic logo"
description = "Set the default logo style for all users on this server. Users can override this setting in their personal preferences."
label = "Logo Style"
modern = "Modern"
modernAlt = "Modern logo"
[admin.settings.general.showUpdate]
description = "Display notifications when a new version is available"
label = "Show Update Notifications"
@@ -1,25 +0,0 @@
{
"short_name": "Stirling PDF",
"name": "Stirling PDF",
"icons": [
{
"src": "classic-logo/favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "classic-logo/logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "classic-logo/logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
@@ -7,11 +7,6 @@ import modernMarkLight from "@app/assets/brand/modern-logo/StirlingPDFLogoNoText
import modernBlack from "@app/assets/brand/modern-logo/StirlingPDFLogoBlackText.svg";
import modernWhite from "@app/assets/brand/modern-logo/StirlingPDFLogoWhiteText.svg";
import modernGrey from "@app/assets/brand/modern-logo/StirlingPDFLogoGreyText.svg";
import classicMarkDark from "@app/assets/brand/classic-logo/StirlingPDFLogoNoTextDark.svg";
import classicMarkLight from "@app/assets/brand/classic-logo/StirlingPDFLogoNoTextLight.svg";
import classicBlack from "@app/assets/brand/classic-logo/StirlingPDFLogoBlackText.svg";
import classicWhite from "@app/assets/brand/classic-logo/StirlingPDFLogoWhiteText.svg";
import classicGrey from "@app/assets/brand/classic-logo/StirlingPDFLogoGreyText.svg";
// Fixed swatch so the light-on-dark mark variant previews on a dark
// surface in either theme.
@@ -33,18 +28,6 @@ const SETS: VariantSet[] = [
{ label: "WhiteText", src: modernWhite, onDark: true },
],
},
{
variant: "classic",
mark: [
{ label: "NoTextDark", src: classicMarkDark },
{ label: "NoTextLight", src: classicMarkLight, onDark: true },
],
wordmark: [
{ label: "BlackText", src: classicBlack },
{ label: "GreyText", src: classicGrey },
{ label: "WhiteText", src: classicWhite, onDark: true },
],
},
];
function Swatch({ label, src, onDark, h }: Asset & { h: number }) {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.2 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.4 KiB

@@ -1,33 +0,0 @@
<svg width="82" height="92" viewBox="0 0 82 92" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_dddd_3933_98714)">
<path d="M33.0874 84.4375L70.2191 75.6383L77.157 43.9963L33.0874 84.4375Z" fill="#D0DBDC"/>
<path d="M56.3682 0.557473C59.916 -0.147829 63.2976 2.34683 63.2422 5.67271L63.2139 8.04087L68.7031 6.95689C72.7776 6.15095 76.6575 8.97364 76.7129 12.7782L77.1016 44.0213L54.9326 64.6639L33.084 84.4374L8.27637 81.4891C4.06313 81.0356 0.875008 77.7854 0.875 73.9051V19.3036C0.875108 14.8438 4.28482 10.9886 9.08008 10.0311L56.3682 0.557473ZM46.2773 19.7586C38.7933 16.1052 27.8721 18.8006 27.8721 18.8006C19.5566 21.1692 16.6184 26.6627 15.5928 30.5682C14.8168 33.5665 15.0111 36.6907 16.1475 39.5126C18.559 45.4589 24.4073 47.2732 30.6162 49.037C38.3528 51.2221 36.8902 55.2099 36.8809 55.2352C35.9939 57.9815 33.8044 58.8885 31.9473 59.1405C30.4782 59.3421 28.9533 59.1405 27.5674 58.6366C20.0835 55.8397 17.4502 50.0448 17.4502 50.0448H16.1191L16.6182 63.0711C19.3345 64.6081 24.8785 65.8934 28.0107 66.5233C30.2837 67.002 32.6404 67.1534 34.9688 66.9266C48.1902 65.6162 48.9941 54.202 48.9941 54.202C50.4076 42.0323 38.8767 39.0091 31.8916 37.5477C30.589 37.2706 29.5911 36.9174 28.8428 36.4891C25.6828 34.7505 26.0434 30.3658 29.4805 28.8036C31.8088 27.7456 34.7749 28.0729 36.7705 28.9295C39.9027 30.2903 44.1436 35.3045 44.1436 35.3045H45.9453L46.2773 19.7586Z" fill="white"/>
<path d="M63.2779 8.03641L63.2781 57.7846L77.1651 44.2035L76.7771 11.9265C76.7494 8.65091 73.3953 6.02067 69.875 6.72618L63.2779 8.03641Z" fill="#D3D3D3"/>
</g>
<defs>
<filter id="filter0_dddd_3933_98714" x="-8.34465e-07" y="-4.17233e-07" width="81.1025" height="91.875" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.4375"/>
<feGaussianBlur stdDeviation="0.4375"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3933_98714"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="0.4375" dy="1.3125"/>
<feGaussianBlur stdDeviation="0.656251"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.09 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_3933_98714" result="effect2_dropShadow_3933_98714"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="0.875001" dy="3.0625"/>
<feGaussianBlur stdDeviation="0.875001"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="effect2_dropShadow_3933_98714" result="effect3_dropShadow_3933_98714"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="1.75" dy="5.25"/>
<feGaussianBlur stdDeviation="1.09375"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.01 0"/>
<feBlend mode="normal" in2="effect3_dropShadow_3933_98714" result="effect4_dropShadow_3933_98714"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect4_dropShadow_3933_98714" result="shape"/>
</filter>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

@@ -1,34 +0,0 @@
<svg width="82" height="92" viewBox="0 0 82 92" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.2" filter="url(#filter0_dddd_3933_98709)">
<path d="M56.3678 0.557543L9.07973 10.0315C4.2844 10.989 0.875 14.8441 0.875 19.3039V73.9053C0.875 77.7856 4.06265 81.0359 8.27589 81.4895L33.0841 84.4375L54.9327 64.6642L77.1014 44.0219L76.7133 12.778C76.6579 8.97326 72.7773 6.15122 68.7027 6.95752L63.2143 8.04098L63.2421 5.67248C63.2975 2.34651 59.9158 -0.147965 56.3678 0.557543Z" fill="#FF4B4B"/>
<path d="M63.269 8.04043V57.5772L77.1561 43.9962L76.7681 11.7192C76.7403 8.44358 73.3864 6.02469 69.8661 6.7302L63.269 8.04043Z" fill="#545454"/>
<path d="M33.0873 84.4375L70.2189 75.6383L77.1569 43.9963L33.0873 84.4375Z" fill="#D0DBDC"/>
<path d="M44.1432 35.3049C44.1432 35.3049 39.9022 30.2907 36.77 28.9301C34.7743 28.0734 31.8084 27.7459 29.48 28.8041C26.0429 30.3663 25.6826 34.7506 28.8425 36.4891C29.5909 36.9175 30.5888 37.2702 31.8915 37.5474C38.8767 39.0088 50.4076 42.0324 48.994 54.2024C48.994 54.2024 48.1901 65.6166 34.9683 66.9268C32.6399 67.1536 30.2839 67.0024 28.0109 66.5236C24.8787 65.8937 19.335 64.6087 16.6185 63.0717L16.1196 50.045H17.4501C17.4501 50.045 20.0834 55.8402 27.5674 58.6371C28.9534 59.141 30.4779 59.3426 31.947 59.141C33.8041 58.889 35.9939 57.982 36.8809 55.2355C36.8809 55.2355 38.3777 51.2292 30.6165 49.0371C24.4075 47.2733 18.5589 45.4592 16.1473 39.5127C15.0109 36.6907 14.8168 33.5663 15.593 30.5679C16.6185 26.6624 19.5567 21.1695 27.8723 18.801C27.8723 18.801 38.7935 16.105 46.2776 19.7585L45.9449 35.3049H44.1432Z" fill="white"/>
</g>
<defs>
<filter id="filter0_dddd_3933_98709" x="-8.34465e-07" y="-4.17233e-07" width="81.0944" height="91.875" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.4375"/>
<feGaussianBlur stdDeviation="0.4375"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3933_98709"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="0.4375" dy="1.3125"/>
<feGaussianBlur stdDeviation="0.656251"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.09 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_3933_98709" result="effect2_dropShadow_3933_98709"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="0.875001" dy="3.0625"/>
<feGaussianBlur stdDeviation="0.875001"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="effect2_dropShadow_3933_98709" result="effect3_dropShadow_3933_98709"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="1.75" dy="5.25"/>
<feGaussianBlur stdDeviation="1.09375"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.01 0"/>
<feBlend mode="normal" in2="effect3_dropShadow_3933_98709" result="effect4_dropShadow_3933_98709"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect4_dropShadow_3933_98709" result="shape"/>
</filter>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="77" height="84" fill="none" viewBox="0 0 77 84"><path fill="#D0DBDC" d="M32.2124 83.9997L69.3441 75.2005L76.282 43.5585L32.2124 83.9997Z"/><path fill="#fff" d="M55.4932 0.119995C59.0409 -0.585293 62.4223 1.9086 62.3672 5.23425L62.3389 7.60339L67.8281 6.51941C71.9024 5.71349 75.7821 8.53542 75.8379 12.3397L76.2266 43.5839L54.0576 64.2264L32.209 83.9999L7.40137 81.0516C3.18818 80.5981 8.70906e-05 77.3479 0 73.4677V18.8661C3.4631e-05 14.4063 3.40977 10.5511 8.20508 9.59363L55.4932 0.119995ZM45.4023 19.3212C37.9183 15.6676 26.9971 18.3632 26.9971 18.3632C18.6818 20.7316 15.7434 26.2243 14.7178 30.1298C13.9417 33.1282 14.136 36.253 15.2725 39.0751C17.684 45.0213 23.5324 46.8357 29.7412 48.5995C37.5024 50.7916 36.0059 54.7977 36.0059 54.7977C35.1189 57.544 32.9294 58.451 31.0723 58.703C29.6032 58.9046 28.0783 58.703 26.6924 58.1991C19.2266 55.4091 16.588 49.6354 16.5752 49.6073H15.2441L15.7432 62.6337C18.4595 64.1706 24.0035 65.4559 27.1357 66.0858C29.4087 66.5646 31.7654 66.7159 34.0938 66.4891C47.3152 65.1787 48.1191 53.7645 48.1191 53.7645C49.5327 41.5946 38.0017 38.5707 31.0166 37.1093C29.714 36.8321 28.7161 36.4799 27.9678 36.0516C24.8078 34.3131 25.1684 29.9283 28.6055 28.3661C30.9338 27.3081 33.8999 27.6354 35.8955 28.4921C39.0277 29.8529 43.2686 34.8671 43.2686 34.8671H45.0703L45.4023 19.3212Z"/><path fill="#EEE" d="M62.394 7.60263V57.1394L76.2811 43.5584L75.8931 11.2814C75.8653 8.00577 72.5114 5.58689 68.9911 6.2924L62.394 7.60263Z"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

@@ -1,14 +0,0 @@
import type { LogoVariant } from "@app/services/preferencesService";
export const LOGO_FOLDER_BY_VARIANT: Record<LogoVariant, string> = {
modern: "modern-logo",
classic: "classic-logo",
};
export const ensureLogoVariant = (value?: string | null): LogoVariant => {
return value === "classic" ? "classic" : "modern";
};
export const getLogoFolder = (variant?: LogoVariant | null): string => {
return LOGO_FOLDER_BY_VARIANT[ensureLogoVariant(variant)];
};
@@ -1,20 +1,18 @@
import { describe, expect, test } from "vitest";
import * as fs from "fs";
import * as path from "path";
import { LOGO_FOLDER_BY_VARIANT } from "@app/constants/logo";
import type { LogoVariant } from "@app/services/preferencesService";
/**
* Tests that all required logo assets exist for each logo variant.
* Tests that all required logo assets exist.
* This ensures that when useLogoAssets returns paths, those files actually exist.
*/
describe("useLogoAssets - Logo Asset Files", () => {
const publicDir = path.resolve(__dirname, "../../../public");
// Brand logo assets live in core; the editor's vite config copies
// core/assets/brand/<folder>/* into the served root at build time (see
// core/assets/brand/modern-logo/* into the served root at build time (see
// viteStaticCopy in editor/vite.config.ts), so useLogoAssets can keep
// referencing them by their public-URL path. Validate them at source.
const brandDir = path.resolve(__dirname, "../assets/brand");
const logoDir = path.resolve(__dirname, "../assets/brand/modern-logo");
// All asset files that useLogoAssets references
const requiredAssets = [
@@ -28,34 +26,20 @@ describe("useLogoAssets - Logo Asset Files", () => {
"StirlingPDFLogoGreyText.svg",
];
const logoVariants: LogoVariant[] = ["modern", "classic"];
describe.each(logoVariants)("%s logo variant", (variant) => {
const folder = LOGO_FOLDER_BY_VARIANT[variant];
const folderPath = path.join(brandDir, folder);
test(`folder "${folder}" should exist`, () => {
expect(fs.existsSync(folderPath)).toBe(true);
});
test.each(requiredAssets)("should have %s", (assetName) => {
const assetPath = path.join(folderPath, assetName);
expect(
fs.existsSync(assetPath),
`Missing asset: ${folder}/${assetName}`,
).toBe(true);
});
test("logo folder should exist", () => {
expect(fs.existsSync(logoDir)).toBe(true);
});
describe("manifest files", () => {
test("manifest.json should exist for modern variant", () => {
const manifestPath = path.join(publicDir, "manifest.json");
expect(fs.existsSync(manifestPath)).toBe(true);
});
test.each(requiredAssets)("should have %s", (assetName) => {
const assetPath = path.join(logoDir, assetName);
expect(
fs.existsSync(assetPath),
`Missing asset: modern-logo/${assetName}`,
).toBe(true);
});
test("manifest-classic.json should exist for classic variant", () => {
const manifestPath = path.join(publicDir, "manifest-classic.json");
expect(fs.existsSync(manifestPath)).toBe(true);
});
test("manifest.json should exist", () => {
const manifestPath = path.join(publicDir, "manifest.json");
expect(fs.existsSync(manifestPath)).toBe(true);
});
});
@@ -1,18 +1,13 @@
import { useMemo } from "react";
import { BASE_PATH } from "@app/constants/app";
import { getLogoFolder } from "@app/constants/logo";
import { useLogoVariant } from "@app/hooks/useLogoVariant";
const LOGO_FOLDER = "modern-logo";
export function useLogoAssets() {
const logoVariant = useLogoVariant();
return useMemo(() => {
const folder = getLogoFolder(logoVariant);
const folderPath = `${BASE_PATH}/${folder}`;
const folderPath = `${BASE_PATH}/${LOGO_FOLDER}`;
return {
logoVariant,
folder,
folderPath,
getAssetPath: (name: string) => `${folderPath}/${name}`,
wordmark: {
@@ -25,10 +20,7 @@ export function useLogoAssets() {
favicon: `${folderPath}/favicon.ico`,
logo192: `${folderPath}/logo192.png`,
logo512: `${folderPath}/logo512.png`,
manifestHref:
logoVariant === "classic"
? `${BASE_PATH}/manifest-classic.json`
: `${BASE_PATH}/manifest.json`,
manifestHref: `${BASE_PATH}/manifest.json`,
};
}, [logoVariant]);
}, []);
}
@@ -1,7 +1,7 @@
import { useMemo } from "react";
import { useLogoAssets } from "@app/hooks/useLogoAssets";
/** Theme-specific no-text logo SVG URLs under the active variant folder (`modern-logo` / `classic-logo`). */
/** Theme-specific no-text logo SVG URLs under the `modern-logo` folder. */
export function useLogoPath(): { dark: string; light: string } {
const { folderPath } = useLogoAssets();
@@ -1,17 +0,0 @@
import { useMemo } from "react";
import { usePreferences } from "@app/contexts/PreferencesContext";
import { useAppConfig } from "@app/contexts/AppConfigContext";
import type { LogoVariant } from "@app/services/preferencesService";
import { ensureLogoVariant } from "@app/constants/logo";
export function useLogoVariant(): LogoVariant {
const { preferences } = usePreferences();
const { config } = useAppConfig();
return useMemo(() => {
// Check local storage first, then fall back to server config
const preferenceVariant = preferences.logoVariant;
const configVariant = config?.logoStyle;
return ensureLogoVariant(preferenceVariant ?? configVariant);
}, [config?.logoStyle, preferences.logoVariant]);
}
@@ -4,8 +4,6 @@ import {
} from "@app/constants/toolPanel";
import { type ThemeMode } from "@app/constants/theme";
export type LogoVariant = "modern" | "classic";
export type PdfRenderMode = "normal" | "dark" | "sepia";
export type StartupView = "tools" | "read" | "automate";
@@ -42,7 +40,6 @@ export interface UserPreferences {
hasSeenCookieBanner: boolean;
hideUnavailableTools: boolean;
hideUnavailableConversions: boolean;
logoVariant: LogoVariant | null;
pdfRenderMode: PdfRenderMode;
}
@@ -62,7 +59,6 @@ export const DEFAULT_PREFERENCES: UserPreferences = {
hasSeenCookieBanner: false,
hideUnavailableTools: false,
hideUnavailableConversions: false,
logoVariant: null,
pdfRenderMode: "normal",
};
-32
View File
@@ -864,38 +864,6 @@
fill-opacity: 0.7;
}
/* Classic logo icon — single path structure, colors change per theme */
.logo-icon-classic__group {
opacity: 0.2;
}
.logo-icon-classic__body {
fill: var(--p-art-red);
}
.logo-icon-classic__flap {
fill: var(--p-c-545454);
}
.logo-icon-classic__triangle {
fill: var(--p-c-d0dbdc);
}
.logo-icon-classic__s {
fill: white;
}
[data-mantine-color-scheme="dark"] .logo-icon-classic__group {
opacity: 1;
}
[data-mantine-color-scheme="dark"] .logo-icon-classic__body {
fill: white;
}
[data-mantine-color-scheme="dark"] .logo-icon-classic__flap {
fill: var(--p-c-d3d3d3);
}
[data-mantine-color-scheme="dark"] .logo-icon-classic__triangle {
fill: var(--p-c-d0dbdc);
}
[data-mantine-color-scheme="dark"] .logo-icon-classic__s {
fill: var(--mantine-color-body);
}
/* Wordmark muted variant (empty/placeholder states).
Stirling text: 30% opacity in light mode, full opacity in dark. */
.wordmark__text--muted {
@@ -6,7 +6,6 @@ export interface AppConfig {
appNameNavbar?: string;
languages?: string[];
defaultLocale?: string;
logoStyle?: "modern" | "classic";
enableLogin?: boolean;
showSettingsWhenNoLogin?: boolean;
enableEmailInvites?: boolean;
@@ -14,7 +14,6 @@ import {
Badge,
Select,
} from "@mantine/core";
import { SegmentedControl } from "@app/ui/SegmentedControl";
import { alert } from "@app/components/toast";
import RestartConfirmationModal from "@app/components/shared/config/RestartConfirmationModal";
import { useRestartServer } from "@app/components/shared/config/useRestartServer";
@@ -25,7 +24,6 @@ import { SettingsStickyFooter } from "@app/components/shared/config/SettingsStic
import apiClient from "@app/services/apiClient";
import { useLoginRequired } from "@app/hooks/useLoginRequired";
import LoginRequiredBanner from "@app/components/shared/config/LoginRequiredBanner";
import { usePreferences } from "@app/contexts/PreferencesContext";
import { useUnsavedChanges } from "@app/contexts/UnsavedChangesContext";
import {
supportedLanguages,
@@ -38,7 +36,6 @@ interface GeneralSettingsData {
ui: {
appNameNavbar?: string;
languages?: string[];
logoStyle?: "modern" | "classic";
hideDisabledTools?: {
googleDrive?: boolean;
mobileQRScanner?: boolean;
@@ -83,7 +80,6 @@ export default function AdminGeneralSection() {
closeRestartModal,
restartServer,
} = useRestartServer();
const { preferences, updatePreference } = usePreferences();
const { markClean } = useUnsavedChanges();
const languageOptions = useMemo(
() =>
@@ -200,7 +196,6 @@ export default function AdminGeneralSection() {
// UI settings
"ui.appNameNavbar": settings.ui?.appNameNavbar,
"ui.languages": settings.ui?.languages,
"ui.logoStyle": settings.ui?.logoStyle,
"ui.hideDisabledTools.googleDrive":
settings.ui?.hideDisabledTools?.googleDrive,
"ui.hideDisabledTools.mobileQRScanner":
@@ -338,14 +333,6 @@ export default function AdminGeneralSection() {
}
}, [loginEnabled, fetchSettings]);
// Sync local preference with server setting on initial load
useEffect(() => {
if (loading || !loginEnabled || !settings.ui?.logoStyle) return;
// This ensures localStorage always reflects the server's authoritative value
updatePreference("logoVariant", settings.ui.logoStyle);
}, [loading, loginEnabled, settings.ui?.logoStyle, updatePreference]);
// Handle hash navigation for deep linking to specific fields
useEffect(() => {
if (location.hash && !loading) {
@@ -367,31 +354,6 @@ export default function AdminGeneralSection() {
// Override loading state when login is disabled
const actualLoading = loginEnabled ? loading : false;
// Show the server setting when loaded (for admin config), otherwise show user's preference
// Note: User's preference in localStorage is separate and takes precedence in the app via useLogoVariant hook
const logoStyleValue = loginEnabled
? (settings.ui?.logoStyle ?? preferences.logoVariant ?? "modern")
: (preferences.logoVariant ?? "modern");
const handleLogoStyleChange = (value: string) => {
const nextValue = value === "classic" ? "classic" : "modern";
// Only update local settings state - don't update the actual preference until save
// When login is disabled, update preference immediately since there's no server to save to
if (!loginEnabled) {
updatePreference("logoVariant", nextValue);
return;
}
setSettings({
...settings,
ui: {
...settings.ui,
logoStyle: nextValue,
},
});
};
const handleSave = async () => {
// Block save if login is disabled
if (!validateLoginEnabled()) {
@@ -404,11 +366,6 @@ export default function AdminGeneralSection() {
await saveSettings();
// Update local preference after successful save so the app reflects the saved logo style
if (settings.ui?.logoStyle) {
updatePreference("logoVariant", settings.ui.logoStyle);
}
markClean();
showRestartModal();
} catch (_error) {
@@ -481,85 +438,6 @@ export default function AdminGeneralSection() {
/>
</div>
<div>
<Text component="div" size="sm" fw={500} mb={4}>
<Group gap="xs">
<span>
{t("admin.settings.general.logoStyle.label", "Logo Style")}
</span>
<PendingBadge show={isFieldPending("ui.logoStyle")} />
</Group>
</Text>
<Text size="xs" c="dimmed" mb="xs">
{t(
"admin.settings.general.logoStyle.description",
"Choose between the modern minimalist logo or the classic S icon",
)}
</Text>
<SegmentedControl
value={logoStyleValue}
onChange={handleLogoStyleChange}
options={[
{
value: "classic",
label: (
<div
style={{
display: "flex",
alignItems: "center",
gap: "0.5rem",
padding: "0.25rem 0",
}}
>
<img
src="classic-logo/favicon.ico"
alt={t(
"admin.settings.general.logoStyle.classicAlt",
"Classic logo",
)}
style={{ width: "24px", height: "24px" }}
/>
<span>
{t(
"admin.settings.general.logoStyle.classic",
"Classic",
)}
</span>
</div>
),
},
{
value: "modern",
label: (
<div
style={{
display: "flex",
alignItems: "center",
gap: "0.5rem",
padding: "0.25rem 0",
}}
>
<img
src="modern-logo/StirlingPDFLogoNoTextLight.svg"
alt={t(
"admin.settings.general.logoStyle.modernAlt",
"Modern logo",
)}
style={{ width: "24px", height: "24px" }}
/>
<span>
{t(
"admin.settings.general.logoStyle.modern",
"Modern",
)}
</span>
</div>
),
},
]}
/>
</div>
<div>
<MultiSelect
label={
@@ -1,18 +0,0 @@
import type { LogoVariant } from "@app/services/preferencesService";
/**
* SaaS ships only the modern logo — the classic variant is never shown.
*
* This shadows the core hook (which resolves the variant from the stored user
* preference / server `logoStyle`) so that every logo asset in the SaaS bundle
* resolves to the `modern-logo` folder. All logo rendering funnels through this
* hook via `useLogoAssets` / `useLogoPath` (favicon, web manifest, apple-touch
* icon, wordmark, logo icon, tooltip logo) and the login-carousel slides, so
* forcing the variant here is sufficient to keep the classic logo out of SaaS.
*
* The core variant system is intentionally left intact for the OSS and
* proprietary builds.
*/
export function useLogoVariant(): LogoVariant {
return "modern";
}
+3 -7
View File
@@ -320,13 +320,9 @@ export default defineConfig(async ({ mode, command }) => {
dest: "pdfjs/standard_fonts",
},
{
// Brand assets live in core; the editor serves them by URL per
// variant, so copy each set to the /{variant}-logo path its
// manifests, index.html and useLogoAssets resolve against.
src: "src/core/assets/brand/classic-logo/*",
dest: "classic-logo",
},
{
// Brand assets live in core; the editor serves them by URL, so
// copy the set to the /modern-logo path its manifest, index.html
// and useLogoAssets resolve against.
src: "src/core/assets/brand/modern-logo/*",
dest: "modern-logo",
},
-1
View File
@@ -68,7 +68,6 @@ ignore = [
'admin.settings.connections.oauth2.label',
'admin.settings.connections.saml2.label',
'admin.settings.database.port.label',
'admin.settings.general.logoStyle.modern',
'admin.settings.general.system',
'admin.settings.legal.impressum.label',
'admin.settings.premium.title',
-1
View File
@@ -266,7 +266,6 @@ autoPipeline:
ui:
appNameNavbar: "" # name displayed on the navigation bar
logoStyle: modern # Options: 'modern' (default - minimalist logo) or 'classic' (legacy S icon)
languages: [] # If empty, all languages are enabled. To restrict to specific languages, use a whitelist like ["de_DE", "pl_PL", "sv_SE"]. Empty list or not restricting any languages will enable all available languages.
defaultHideUnavailableTools: false # Default user preference: hide disabled tools instead of greying them out
defaultHideUnavailableConversions: false # Default user preference: hide disabled conversion options instead of greying them out