diff --git a/.github/aur/stirling-pdf-desktop/PKGBUILD b/.github/aur/stirling-pdf-desktop/PKGBUILD index 706bfaacc1..de60325133 100644 --- a/.github/aur/stirling-pdf-desktop/PKGBUILD +++ b/.github/aur/stirling-pdf-desktop/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Stirling PDF Inc pkgname=stirling-pdf-desktop -pkgver=2.12.0 +pkgver=2.13.0 pkgrel=1 pkgdesc="Locally hosted, web-based PDF manipulation tool (Tauri desktop app, official Stirling PDF Inc build)" arch=('x86_64') diff --git a/.github/aur/stirling-pdf-server-bin/PKGBUILD b/.github/aur/stirling-pdf-server-bin/PKGBUILD index 0622fed1d3..3853bb6256 100644 --- a/.github/aur/stirling-pdf-server-bin/PKGBUILD +++ b/.github/aur/stirling-pdf-server-bin/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Stirling PDF Inc pkgname=stirling-pdf-server-bin -pkgver=2.12.0 +pkgver=2.13.0 pkgrel=1 pkgdesc="Locally hosted, web-based PDF manipulation tool (server JAR, prebuilt)" arch=('any') diff --git a/build.gradle b/build.gradle index 9c69dd3cea..c7413605d1 100644 --- a/build.gradle +++ b/build.gradle @@ -78,7 +78,7 @@ springBoot { allprojects { group = 'stirling.software' - version = '2.12.0' + version = '2.13.0' configurations.configureEach { exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat" diff --git a/frontend/editor/public/locales/en-US/translation.toml b/frontend/editor/public/locales/en-US/translation.toml index 46dd123b3f..c0a97c75b6 100644 --- a/frontend/editor/public/locales/en-US/translation.toml +++ b/frontend/editor/public/locales/en-US/translation.toml @@ -7712,8 +7712,6 @@ allReleases = "All Releases" breaking = "Breaking" breakingChanges = "Breaking Changes" breakingChangesDefault = "This version contains breaking changes." -breakingChangesDetected = "Breaking Changes Detected" -breakingChangesMessage = "Some versions contain breaking changes. Please review the migration guides below before updating." close = "Close" closeModal = "Close update modal" current = "Current Version" diff --git a/frontend/editor/src-tauri/tauri.conf.json b/frontend/editor/src-tauri/tauri.conf.json index c2e84b3ac1..376f1027cd 100644 --- a/frontend/editor/src-tauri/tauri.conf.json +++ b/frontend/editor/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "Stirling-PDF", - "version": "2.12.0", + "version": "2.13.0", "identifier": "stirling.pdf.dev", "build": { "frontendDist": "../dist", diff --git a/frontend/editor/src/core/components/shared/UpdateModal.tsx b/frontend/editor/src/core/components/shared/UpdateModal.tsx index 6931944c42..ca052ae523 100644 --- a/frontend/editor/src/core/components/shared/UpdateModal.tsx +++ b/frontend/editor/src/core/components/shared/UpdateModal.tsx @@ -24,6 +24,7 @@ import { MachineInfo, } from "@app/services/updateService"; import { Z_INDEX_OVER_CONFIG_MODAL } from "@app/styles/zIndex"; +import { openExternal } from "@app/platform/openExternal"; import WarningAmberIcon from "@mui/icons-material/WarningAmber"; import OpenInNewIcon from "@mui/icons-material/OpenInNew"; import DownloadIcon from "@mui/icons-material/Download"; @@ -36,6 +37,19 @@ import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ExpandLessIcon from "@mui/icons-material/ExpandLess"; +/** + * Best-effort Tauri detection without importing `@tauri-apps/api` into the + * core bundle (which must stay runnable on plain web). Tauri v2 injects + * `__TAURI_INTERNALS__` before any user code runs. Mirrors UpdateStartupPopup. + */ +function isRunningInTauri(): boolean { + if (typeof window === "undefined") return false; + return ( + typeof (window as unknown as { __TAURI_INTERNALS__?: unknown }) + .__TAURI_INTERNALS__ !== "undefined" + ); +} + export type DesktopInstallState = | "idle" | "downloading" @@ -197,6 +211,20 @@ const UpdateModal: React.FC = ({ onClose(); }; + // External links (release notes, migration guides, download fallback) use + // real anchors so they open a new tab on web. Inside Tauri the webview traps + // target="_blank", so on desktop we intercept and hand the URL to the OS + // browser via the platform seam. stopPropagation keeps links nested in the + // clickable version-history rows from toggling the row. + const handleExternalLink = + (url: string) => (e: React.MouseEvent) => { + e.stopPropagation(); + if (isRunningInTauri()) { + e.preventDefault(); + void openExternal(url); + } + }; + // Sort versions newest first, skip the latest (already shown in header) const sortedVersions = fullUpdateInfo?.new_versions ? [...fullUpdateInfo.new_versions].sort((a, b) => @@ -323,11 +351,25 @@ const UpdateModal: React.FC = ({ - {/* Priority badge + recommendation — compact single line */} + {/* Priority badge + recommendation — compact single line. When the + update contains breaking changes we surface a compact inline + chip here rather than a separate full-width orange banner; the + specific per-version detail lives in Version History below. */} {getPriorityLabel(updateSummary.max_priority)} + {updateSummary.any_breaking && ( + } + > + {t("update.breaking", "Breaking")} + + )} {updateSummary.recommended_action || t( @@ -362,6 +404,7 @@ const UpdateModal: React.FC = ({ href={WINDOWS_INSTALL_DOCS_URL} target="_blank" rel="noopener noreferrer" + onClick={handleExternalLink(WINDOWS_INSTALL_DOCS_URL)} > {t( "desktopUpdate.blocked.docsLink", @@ -402,6 +445,9 @@ const UpdateModal: React.FC = ({ component="a" href={`https://github.com/Stirling-Tools/Stirling-PDF/releases/tag/v${updateSummary.latest_version}`} target="_blank" + onClick={handleExternalLink( + `https://github.com/Stirling-Tools/Stirling-PDF/releases/tag/v${updateSummary.latest_version}`, + )} c="blue" style={{ textDecoration: "none", @@ -418,6 +464,9 @@ const UpdateModal: React.FC = ({ component="a" href="https://github.com/Stirling-Tools/Stirling-PDF/releases" target="_blank" + onClick={handleExternalLink( + "https://github.com/Stirling-Tools/Stirling-PDF/releases", + )} c="dimmed" style={{ textDecoration: "none", @@ -433,25 +482,6 @@ const UpdateModal: React.FC = ({ - {/* Breaking changes */} - {updateSummary.any_breaking && ( - } - title={t( - "update.breakingChangesDetected", - "Breaking Changes Detected", - )} - > - {t( - "update.breakingChangesMessage", - "Some versions contain breaking changes. Please review the migration guides below before updating.", - )} - - )} - {/* Migration guides */} {updateSummary.migration_guides && updateSummary.migration_guides.length > 0 && ( @@ -505,6 +535,7 @@ const UpdateModal: React.FC = ({ component="a" href={guide.url} target="_blank" + onClick={handleExternalLink(guide.url)} variant="default" size="xs" rightSection={ @@ -603,7 +634,9 @@ const UpdateModal: React.FC = ({ variant="subtle" size="xs" px={6} - onClick={(e) => e.stopPropagation()} + onClick={handleExternalLink( + `https://github.com/Stirling-Tools/Stirling-PDF/releases/tag/v${version.version}`, + )} rightSection={ } @@ -673,6 +706,10 @@ const UpdateModal: React.FC = ({ version.compatibility.migration_guide_url } target="_blank" + onClick={handleExternalLink( + version.compatibility + .migration_guide_url ?? "", + )} variant="light" color="orange" size="xs" @@ -844,6 +881,7 @@ const UpdateModal: React.FC = ({ component="a" href={downloadUrl} target="_blank" + onClick={handleExternalLink(downloadUrl)} variant="default" radius="md" size="md" @@ -884,6 +922,7 @@ const UpdateModal: React.FC = ({ component="a" href={downloadUrl} target="_blank" + onClick={handleExternalLink(downloadUrl)} color="blue" radius="md" size="lg" diff --git a/frontend/editor/src/core/components/shared/config/configSections/GeneralSection.tsx b/frontend/editor/src/core/components/shared/config/configSections/GeneralSection.tsx index d3383df0c7..e5fdacea79 100644 --- a/frontend/editor/src/core/components/shared/config/configSections/GeneralSection.tsx +++ b/frontend/editor/src/core/components/shared/config/configSections/GeneralSection.tsx @@ -14,7 +14,6 @@ import { ActionIcon, Button, Badge, - Alert, } from "@mantine/core"; import { useTranslation } from "react-i18next"; import { usePreferences } from "@app/contexts/PreferencesContext"; @@ -459,26 +458,6 @@ const GeneralSection: React.FC = ({ /> )} - - {updateSummary?.any_breaking && ( - - - {t( - "update.breakingChangesMessage", - "Some versions contain breaking changes. Please review the migration guides before updating.", - )} - - - )} )} diff --git a/frontend/editor/src/core/platform/openExternal.ts b/frontend/editor/src/core/platform/openExternal.ts new file mode 100644 index 0000000000..0e4f7d1e82 --- /dev/null +++ b/frontend/editor/src/core/platform/openExternal.ts @@ -0,0 +1,12 @@ +/** + * core/web implementation of the @app/platform/openExternal seam. + * + * The OSS / self-hosted web build runs in a normal browser, so an external URL + * just opens in a new tab. The desktop (Tauri) and saas leaves shadow this with + * their own platform/openExternal.ts. This lives in core/ so the seam also + * resolves for the core and proprietary build variants - whose `@app/*` maps to + * src/core - since shared components (e.g. UpdateModal) import it. + */ +export const openExternal = async (url: string): Promise => { + window.open(url, "_blank", "noopener,noreferrer"); +}; diff --git a/frontend/editor/src/core/testing/serverExperienceSimulations.ts b/frontend/editor/src/core/testing/serverExperienceSimulations.ts index 1460071572..eee4f85851 100644 --- a/frontend/editor/src/core/testing/serverExperienceSimulations.ts +++ b/frontend/editor/src/core/testing/serverExperienceSimulations.ts @@ -38,7 +38,7 @@ const FREE_LICENSE_INFO: LicenseInfo = { const BASE_NO_LOGIN_CONFIG: AppConfig = { enableAnalytics: true, - appVersion: "2.12.0", + appVersion: "2.13.0", serverCertificateEnabled: false, enableAlphaFunctionality: false, serverPort: 8080, diff --git a/frontend/editor/src/proprietary/testing/serverExperienceSimulations.ts b/frontend/editor/src/proprietary/testing/serverExperienceSimulations.ts index 6b3b0d4f8f..9fce606968 100644 --- a/frontend/editor/src/proprietary/testing/serverExperienceSimulations.ts +++ b/frontend/editor/src/proprietary/testing/serverExperienceSimulations.ts @@ -48,7 +48,7 @@ const FREE_LICENSE_INFO: LicenseInfo = { const BASE_NO_LOGIN_CONFIG: AppConfig = { enableAnalytics: true, - appVersion: "2.12.0", + appVersion: "2.13.0", serverCertificateEnabled: false, enableAlphaFunctionality: false, enableDesktopInstallSlide: true,