Fix any type usage in desktop/ (#6033)

# Description of Changes
Follow on from #5949, expanding any type usage ban to the `desktop/`
folder

Also gets rid of a bunch of really verbose desktop logging that I don't
think we really need anymore (or ever needed tbh, most of it doesn't
make sense) because it was using a bunch of `any` typing and wasn't
worth fixing.
This commit is contained in:
James Brunton
2026-04-20 12:42:38 +00:00
committed by GitHub
parent 308da01d96
commit cc9650e7a3
13 changed files with 55 additions and 112 deletions
+1
View File
@@ -78,6 +78,7 @@ export default defineConfig(
// Folders that have been cleaned up and are now conformant - stricter rules enforced here // Folders that have been cleaned up and are now conformant - stricter rules enforced here
{ {
files: [ files: [
"src/desktop/**/*.{js,mjs,jsx,ts,tsx}",
"src/proprietary/**/*.{js,mjs,jsx,ts,tsx}", "src/proprietary/**/*.{js,mjs,jsx,ts,tsx}",
"src/saas/**/*.{js,mjs,jsx,ts,tsx}", "src/saas/**/*.{js,mjs,jsx,ts,tsx}",
"src/prototypes/**/*.{js,mjs,jsx,ts,tsx}", "src/prototypes/**/*.{js,mjs,jsx,ts,tsx}",
@@ -95,9 +95,9 @@ export function DesktopOnboardingModal() {
<div className={styles.heroWrapper} style={{ flexShrink: 0 }}> <div className={styles.heroWrapper} style={{ flexShrink: 0 }}>
<AnimatedSlideBackground <AnimatedSlideBackground
gradientStops={ gradientStops={
(step === 0 step === 0
? welcomeSlide.background.gradientStops ? welcomeSlide.background.gradientStops
: SIGN_IN_GRADIENT) as [string, string] : SIGN_IN_GRADIENT
} }
circles={welcomeSlide.background.circles} circles={welcomeSlide.background.circles}
isActive isActive
@@ -24,9 +24,7 @@ export const ServerSelection: React.FC<ServerSelectionProps> = ({
const [securityDisabled, setSecurityDisabled] = useState(false); const [securityDisabled, setSecurityDisabled] = useState(false);
const serverUrl = localStorage.getItem("server_url") || ""; const serverUrl = localStorage.getItem("server_url") || "";
const handleSubmit = async (e: React.FormEvent) => { const submitServerUrl = async () => {
e.preventDefault();
// Normalize and validate URL // Normalize and validate URL
let url = customUrl.trim().replace(/\/+$/, "") || serverUrl; let url = customUrl.trim().replace(/\/+$/, "") || serverUrl;
@@ -243,7 +241,12 @@ export const ServerSelection: React.FC<ServerSelectionProps> = ({
}; };
return ( return (
<form onSubmit={handleSubmit}> <form
onSubmit={(e) => {
e.preventDefault();
void submitServerUrl();
}}
>
<Stack gap="md"> <Stack gap="md">
<TextInput <TextInput
label={t("setup.server.url.label", "Server URL")} label={t("setup.server.url.label", "Server URL")}
@@ -321,7 +324,7 @@ export const ServerSelection: React.FC<ServerSelectionProps> = ({
setCustomUrl(serverUrl); setCustomUrl(serverUrl);
// Auto-submit the form after setting the URL // Auto-submit the form after setting the URL
setTimeout(() => { setTimeout(() => {
handleSubmit(new Event("submit") as any); void submitServerUrl();
}, 0); }, 0);
}} }}
> >
@@ -140,7 +140,7 @@ export function SelfHostedOfflineBanner() {
toolAvailability[id]?.reason === "selfHostedOffline", toolAvailability[id]?.reason === "selfHostedOffline",
) )
.map((id) => toolRegistry[id]?.name ?? id) .map((id) => toolRegistry[id]?.name ?? id)
.filter(Boolean) as string[]; .filter(Boolean);
// Use translated tool names from the registry as prefixes // Use translated tool names from the registry as prefixes
const convertPrefix = toolRegistry["convert" as ToolId]?.name ?? "Convert"; const convertPrefix = toolRegistry["convert" as ToolId]?.name ?? "Convert";
@@ -3,12 +3,10 @@ import CoreToolButton from "@core/components/tools/toolPicker/ToolButton";
import { getToolDisabledReason } from "@app/components/tools/fullscreen/shared"; import { getToolDisabledReason } from "@app/components/tools/fullscreen/shared";
import { useToolWorkflow } from "@app/contexts/ToolWorkflowContext"; import { useToolWorkflow } from "@app/contexts/ToolWorkflowContext";
import { useAppConfig } from "@app/contexts/AppConfigContext"; import { useAppConfig } from "@app/contexts/AppConfigContext";
import { ToolRegistryEntry } from "@app/data/toolsTaxonomy";
import { import {
connectionModeService, connectionModeService,
type ConnectionMode, type ConnectionMode,
} from "@app/services/connectionModeService"; } from "@app/services/connectionModeService";
import type { ToolId } from "@app/types/toolId";
type CoreToolButtonProps = React.ComponentProps<typeof CoreToolButton>; type CoreToolButtonProps = React.ComponentProps<typeof CoreToolButton>;
@@ -35,8 +33,8 @@ const ToolButton: React.FC<CoreToolButtonProps> = (props) => {
}, []); }, []);
const disabledReason = getToolDisabledReason( const disabledReason = getToolDisabledReason(
props.id as string, props.id,
props.tool as ToolRegistryEntry, props.tool,
toolAvailability, toolAvailability,
premiumEnabled, premiumEnabled,
); );
@@ -49,7 +47,7 @@ const ToolButton: React.FC<CoreToolButtonProps> = (props) => {
connectionMode === "local" && connectionMode === "local" &&
disabledReason !== "comingSoon" && disabledReason !== "comingSoon" &&
disabledReason !== "selfHostedOffline" disabledReason !== "selfHostedOffline"
? () => handleToolSelectForced(props.id as ToolId) ? () => handleToolSelectForced(props.id)
: undefined; : undefined;
return ( return (
@@ -15,11 +15,8 @@ import { isTauri } from "@tauri-apps/api/core";
export function getApiBaseUrl(): string { export function getApiBaseUrl(): string {
if (!isTauri()) { if (!isTauri()) {
// Runtime override to fix hardcoded localhost in builds // Runtime override to fix hardcoded localhost in builds
if ( if (typeof window !== "undefined" && window.STIRLING_PDF_API_BASE_URL) {
typeof window !== "undefined" && return window.STIRLING_PDF_API_BASE_URL;
(window as any).STIRLING_PDF_API_BASE_URL
) {
return (window as any).STIRLING_PDF_API_BASE_URL;
} }
return import.meta.env.VITE_API_BASE_URL; return import.meta.env.VITE_API_BASE_URL;
@@ -1,4 +1,4 @@
import { invoke } from "@tauri-apps/api/core"; import { invoke, isTauri } from "@tauri-apps/api/core";
import { fetch } from "@tauri-apps/plugin-http"; import { fetch } from "@tauri-apps/plugin-http";
import { endpointAvailabilityService } from "@app/services/endpointAvailabilityService"; import { endpointAvailabilityService } from "@app/services/endpointAvailabilityService";
import { selfHostedServerMonitor } from "@app/services/selfHostedServerMonitor"; import { selfHostedServerMonitor } from "@app/services/selfHostedServerMonitor";
@@ -240,90 +240,11 @@ export class ConnectionModeService {
*/ */
async testConnection(url: string): Promise<ConnectionTestResult> { async testConnection(url: string): Promise<ConnectionTestResult> {
console.log( console.log(
`[ConnectionModeService] 🔍 Starting comprehensive connection diagnostics for: ${url}`, `[ConnectionModeService] 🔍 Starting connection diagnostics for: ${url}`,
); );
console.log( console.log(
`[ConnectionModeService] ==================== DIAGNOSTIC SESSION START ====================`, `[ConnectionModeService] System: online=${navigator.onLine}, tauri=${isTauri()}, concurrency=${navigator.hardwareConcurrency || "unknown"}`,
); );
console.log(`[ConnectionModeService] System Information:`);
console.log(
`[ConnectionModeService] - User Agent: ${navigator.userAgent}`,
);
console.log(`[ConnectionModeService] - Platform: ${navigator.platform}`);
console.log(`[ConnectionModeService] - Online: ${navigator.onLine}`);
console.log(
`[ConnectionModeService] - Connection Type: ${(navigator as any).connection?.effectiveType || "unknown"}`,
);
console.log(`[ConnectionModeService] - Language: ${navigator.language}`);
console.log(
`[ConnectionModeService] - Cookies Enabled: ${navigator.cookieEnabled}`,
);
console.log(
`[ConnectionModeService] - Hardware Concurrency: ${navigator.hardwareConcurrency || "unknown"} cores`,
);
console.log(
`[ConnectionModeService] - Max Touch Points: ${navigator.maxTouchPoints}`,
);
// Check for proxy environment variables
console.log(`[ConnectionModeService] Environment Check:`);
const envProxy =
(window as any).process?.env?.HTTP_PROXY ||
(window as any).process?.env?.HTTPS_PROXY;
if (envProxy) {
console.log(`[ConnectionModeService] - Proxy detected: ${envProxy}`);
} else {
console.log(
`[ConnectionModeService] - No proxy environment variables detected`,
);
}
// Check if running in Tauri (v2 uses different detection)
console.log(`[ConnectionModeService] - Checking Tauri context...`);
console.log(
`[ConnectionModeService] - window.__TAURI__ type: ${typeof (window as any).__TAURI__}`,
);
console.log(
`[ConnectionModeService] - window.__TAURI_INTERNALS__ type: ${typeof (window as any).__TAURI_INTERNALS__}`,
);
console.log(
`[ConnectionModeService] - window.location.href:`,
window.location.href,
);
console.log(
`[ConnectionModeService] - window.location.protocol:`,
window.location.protocol,
);
// Tauri v2 detection: check for __TAURI_INTERNALS__ or tauri:// protocol
const isTauriV2 =
typeof (window as any).__TAURI_INTERNALS__ !== "undefined" ||
window.location.protocol === "tauri:" ||
window.location.hostname === "tauri.localhost";
const isTauriV1 = typeof (window as any).__TAURI__ !== "undefined";
const isTauri = isTauriV1 || isTauriV2;
console.log(
`[ConnectionModeService] - Running in Tauri v1: ${isTauriV1}`,
);
console.log(
`[ConnectionModeService] - Running in Tauri v2: ${isTauriV2}`,
);
console.log(`[ConnectionModeService] - Running in Tauri: ${isTauri}`);
if (isTauri) {
if (isTauriV1) {
const tauriApi = (window as any).__TAURI__;
console.log(`[ConnectionModeService] - Tauri v1 API:`, tauriApi);
}
if (isTauriV2) {
console.log(
`[ConnectionModeService] - Tauri v2 detected via internals/protocol`,
);
const internals = (window as any).__TAURI_INTERNALS__;
console.log(`[ConnectionModeService] - Tauri internals:`, internals);
}
}
const diagnostics: DiagnosticResult[] = []; const diagnostics: DiagnosticResult[] = [];
const healthUrl = `${url.replace(/\/$/, "")}/api/v1/info/status`; const healthUrl = `${url.replace(/\/$/, "")}/api/v1/info/status`;
@@ -723,7 +644,13 @@ export class ConnectionModeService {
`[ConnectionModeService] - Certificate validation: ${disableCertValidation ? "DISABLED" : "ENABLED"}`, `[ConnectionModeService] - Certificate validation: ${disableCertValidation ? "DISABLED" : "ENABLED"}`,
); );
const fetchOptions: any = { const fetchOptions: RequestInit & {
connectTimeout?: number;
danger?: {
acceptInvalidCerts: boolean;
acceptInvalidHostnames: boolean;
};
} = {
method: "GET", method: "GET",
connectTimeout: 10000, connectTimeout: 10000,
}; };
@@ -200,11 +200,14 @@ export class EndpointAvailabilityService {
const data = await response.json(); const data = await response.json();
const now = Date.now(); const now = Date.now();
Object.entries(data).forEach(([endpoint, details]: [string, any]) => { Object.entries(data).forEach(
const available = details?.enabled ?? false; ([endpoint, details]: [string, unknown]) => {
this.localCache.set(endpoint, available); const available =
this.localCacheExpiry.set(endpoint, now + this.CACHE_DURATION); (details as { enabled?: boolean })?.enabled ?? false;
}); this.localCache.set(endpoint, available);
this.localCacheExpiry.set(endpoint, now + this.CACHE_DURATION);
},
);
} else { } else {
console.warn( console.warn(
`[endpointAvailabilityService] Failed to preload endpoints: ${response.status}`, `[endpointAvailabilityService] Failed to preload endpoints: ${response.status}`,
@@ -313,5 +316,5 @@ export const endpointAvailabilityService = new EndpointAvailabilityService();
// Expose to window for debugging // Expose to window for debugging
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
(window as any).endpointAvailabilityService = endpointAvailabilityService; window.endpointAvailabilityService = endpointAvailabilityService;
} }
@@ -1,3 +1,4 @@
import { isAxiosError } from "axios";
import { handleHttpError as coreHandleHttpError } from "@core/services/httpErrorHandler"; import { handleHttpError as coreHandleHttpError } from "@core/services/httpErrorHandler";
/** /**
@@ -6,8 +7,8 @@ import { handleHttpError as coreHandleHttpError } from "@core/services/httpError
* login page must not appear. Instead, open the SignInModal for re-authentication. * login page must not appear. Instead, open the SignInModal for re-authentication.
* All other error handling delegates to the core implementation. * All other error handling delegates to the core implementation.
*/ */
export async function handleHttpError(error: any): Promise<boolean> { export async function handleHttpError(error: unknown): Promise<boolean> {
const status: number | undefined = error?.response?.status; const status = isAxiosError(error) ? error.response?.status : undefined;
if (status === 401) { if (status === 401) {
// In desktop builds, 401s are handled by the auth service (token refresh + toast // In desktop builds, 401s are handled by the auth service (token refresh + toast
@@ -98,7 +98,7 @@ export async function saveMultipleFilesWithPrompt(
try { try {
const fileName = const fileName =
file instanceof File ? file.name : `output_${savedCount + 1}.pdf`; file instanceof File ? file.name : `output_${savedCount + 1}.pdf`;
const filePath = await join(selectedFolder as string, fileName); const filePath = await join(selectedFolder, fileName);
const arrayBuffer = await file.arrayBuffer(); const arrayBuffer = await file.arrayBuffer();
await writeFile(filePath, new Uint8Array(arrayBuffer)); await writeFile(filePath, new Uint8Array(arrayBuffer));
savedCount++; savedCount++;
@@ -1,3 +1,4 @@
import { isAxiosError } from "axios";
import { extractAxiosErrorMessage } from "@app/services/httpErrorUtils"; import { extractAxiosErrorMessage } from "@app/services/httpErrorUtils";
import { alert } from "@app/components/toast"; import { alert } from "@app/components/toast";
@@ -12,7 +13,11 @@ import { alert } from "@app/components/toast";
* false if this is not a SaaS error. * false if this is not a SaaS error.
*/ */
export function handleSaaSError(error: unknown): boolean { export function handleSaaSError(error: unknown): boolean {
if ((error as any)?.config?._isSaaSRequest !== true) return false; if (
!isAxiosError(error) ||
(error.config as { _isSaaSRequest?: boolean })?._isSaaSRequest !== true
)
return false;
const { title: originalTitle, body: originalBody } = const { title: originalTitle, body: originalBody } =
extractAxiosErrorMessage(error); extractAxiosErrorMessage(error);
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any -- Axios-compatible API requires matching axios's `any` signatures */
import { fetch } from "@tauri-apps/plugin-http"; import { fetch } from "@tauri-apps/plugin-http";
/** /**
@@ -231,7 +232,12 @@ class TauriHttpClient {
// Make the request using Tauri's native HTTP client (standard Fetch API) // Make the request using Tauri's native HTTP client (standard Fetch API)
// Enable certificate bypass for HTTPS to handle missing intermediate certs and self-signed certs // Enable certificate bypass for HTTPS to handle missing intermediate certs and self-signed certs
const fetchOptions: any = { const fetchOptions: RequestInit & {
danger?: {
acceptInvalidCerts: boolean;
acceptInvalidHostnames: boolean;
};
} = {
method, method,
headers, headers,
body, body,
+2
View File
@@ -15,6 +15,8 @@ declare module "assets/material-symbols-icons.json" {
declare global { declare global {
interface Window { interface Window {
__STIRLING_PDF_BASE_URL__?: string; __STIRLING_PDF_BASE_URL__?: string;
STIRLING_PDF_API_BASE_URL?: string;
endpointAvailabilityService?: unknown;
} }
} }