Fix bad frontend architecture (#6730)

# Description of Changes
#6727 introduced frontend code which goes against the architecture, so
this PR re-implements it in the architecture properly, along with
another bad Tauri check that I found in the source. I also updated the
`AGENTS.md` file to use Claude's "read this file" syntax to try and
force AI to actually read the file instead of just suggesting that it
does it.
This commit is contained in:
James Brunton
2026-06-19 12:34:13 +00:00
committed by GitHub
parent 66841db2b7
commit 3793a6df52
6 changed files with 50 additions and 47 deletions
+1 -1
View File
@@ -152,7 +152,7 @@ The project structure is defined in `engine/pyproject.toml`. Any new dependencie
#### Import Paths - CRITICAL
**ALWAYS use `@app/*` for imports.** Do not use `@core/*` or `@proprietary/*` unless explicitly wrapping/extending a lower layer implementation.
For a broader explanation of the frontend layering and override architecture, see [frontend/editor/DeveloperGuide.md](frontend/editor/DeveloperGuide.md).
For a broader explanation of the frontend layering and override architecture, read @frontend/editor/DeveloperGuide.md
```typescript
// ✅ CORRECT - Use @app/* for all imports
@@ -24,7 +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 { handleExternalLinkClick } from "@app/platform/externalLinkClick";
import WarningAmberIcon from "@mui/icons-material/WarningAmber";
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
import DownloadIcon from "@mui/icons-material/Download";
@@ -37,19 +37,6 @@ 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"
@@ -211,18 +198,10 @@ const UpdateModal: React.FC<UpdateModalProps> = ({
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<HTMLElement>) => {
e.stopPropagation();
if (isRunningInTauri()) {
e.preventDefault();
void openExternal(url);
}
handleExternalLinkClick(url, e);
};
// Sort versions newest first, skip the latest (already shown in header)
@@ -20,29 +20,9 @@ const STARTUP_DELAY_MS = 15_000;
const SNOOZE_KEY = "stirling-pdf-updater:snoozedUntil";
const SNOOZE_DURATION_MS = 24 * 60 * 60 * 1000;
/**
* Best-effort Tauri detection without importing `@tauri-apps/api` into the
* core bundle (which must remain runnable on plain web). Tauri v2 injects
* `__TAURI_INTERNALS__` before any user code runs.
*/
function isRunningInTauri(): boolean {
if (typeof window === "undefined") return false;
return (
typeof (window as unknown as { __TAURI_INTERNALS__?: unknown })
.__TAURI_INTERNALS__ !== "undefined"
);
}
/**
* Web/server-side auto-popup that shows the UpdateModal on startup when a
* newer Stirling-PDF version is available. Previously this check only ran
* from the Settings → General "Check for Updates" button, so non-desktop
* users could sit on stale versions indefinitely without any prompt.
*
* On desktop (Tauri) this component is a no-op — `useDesktopUpdatePopup`
* drives the desktop flow because it also has to honour the headless
* `updateMode` provisioning flag and wire up the silent/auto installer.
* Running both would double-popup.
* newer Stirling-PDF version is available.
*/
export function UpdateStartupPopup() {
const { config } = useAppConfig();
@@ -60,8 +40,6 @@ export function UpdateStartupPopup() {
const hasChecked = useRef(false);
useEffect(() => {
// Skip on desktop — the Tauri popup owns that flow end-to-end.
if (isRunningInTauri()) return;
if (hasChecked.current) return;
if (!currentVersion) return;
// Don't even schedule the timer until we have a version to compare.
@@ -0,0 +1,15 @@
import type { MouseEvent } from "react";
/**
* Click handler for external `<a target="_blank">` links rendered by shared
* components (e.g. UpdateModal release notes / migration guides).
*
* In a normal browser a `target="_blank"` anchor already opens the URL in a new
* tab, so this default does nothing and lets the native navigation proceed.
* Builds whose webview traps `target="_blank"` inside the app window shadow this
* module to intercept the click and route the URL to the OS browser instead.
*/
export function handleExternalLinkClick(
_url: string,
_event: MouseEvent<HTMLElement>,
): void {}
@@ -0,0 +1,13 @@
/**
* Desktop (Tauri) override of @app/components/shared/UpdateStartupPopup.
*
* On desktop the update flow is owned end-to-end by `useDesktopUpdatePopup`,
* which also honours the headless `updateMode` provisioning flag and wires up
* the silent/auto installer. The web startup popup must therefore be a no-op
* here, otherwise both would run and double-popup.
*/
export function UpdateStartupPopup() {
return null;
}
export default UpdateStartupPopup;
@@ -0,0 +1,18 @@
import type { MouseEvent } from "react";
import { openExternal } from "@app/platform/openExternal";
/**
* Desktop (Tauri) override of the @app/platform/externalLinkClick seam.
*
* The app runs inside a Tauri webview, which traps a `target="_blank"` anchor
* inside our own window. Intercept the click and hand the URL to the OS browser
* via the openExternal seam (Tauri shell open) so the link lands in the user's
* real browser.
*/
export function handleExternalLinkClick(
url: string,
event: MouseEvent<HTMLElement>,
): void {
event.preventDefault();
void openExternal(url);
}