Compare commits

...
Author SHA1 Message Date
Ludy87 98080a24d4 task frontend:fix 2026-05-13 21:07:45 +02:00
Ludy87 96e03dfe94 handle axios header typing 2026-05-13 21:00:34 +02:00
9 changed files with 209 additions and 160 deletions
+34 -8
View File
@@ -59,7 +59,7 @@
"@tauri-apps/plugin-shell": "^2.3.5",
"@userback/widget": "^0.3.12",
"autoprefixer": "^10.4.21",
"axios": "^1.15.0",
"axios": "^1.15.2",
"d3": "^7.9.0",
"globals": "^17.5.0",
"i18next": "^25.5.2",
@@ -5903,16 +5903,42 @@
}
},
"node_modules/axios": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz",
"integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz",
"integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.11",
"follow-redirects": "^1.16.0",
"form-data": "^4.0.5",
"https-proxy-agent": "^5.0.1",
"proxy-from-env": "^2.1.0"
}
},
"node_modules/axios/node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"license": "MIT",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/axios/node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"license": "MIT",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/axios/node_modules/proxy-from-env": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
@@ -9197,9 +9223,9 @@
}
},
"node_modules/ip-address": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
"integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
"dev": true,
"license": "MIT",
"engines": {
+1 -1
View File
@@ -55,7 +55,7 @@
"@tauri-apps/plugin-shell": "^2.3.5",
"@userback/widget": "^0.3.12",
"autoprefixer": "^10.4.21",
"axios": "^1.15.0",
"axios": "^1.15.2",
"d3": "^7.9.0",
"globals": "^17.5.0",
"i18next": "^25.5.2",
+83 -100
View File
@@ -1,105 +1,88 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "Stirling-PDF",
"version": "2.11.0",
"identifier": "stirling.pdf.dev",
"build": {
"frontendDist": "../dist",
"devUrl": "http://localhost:5173",
"beforeDevCommand": "npx vite --mode desktop",
"beforeBuildCommand": "npx vite build --mode desktop"
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "Stirling-PDF",
"version": "2.11.0",
"identifier": "stirling.pdf.dev",
"build": {
"frontendDist": "../dist",
"devUrl": "http://localhost:5173",
"beforeDevCommand": "npx vite --mode desktop",
"beforeBuildCommand": "npx vite build --mode desktop"
},
"app": {
"windows": [
{
"title": "Stirling-PDF",
"width": 1280,
"height": 800,
"resizable": true,
"fullscreen": false,
"additionalBrowserArgs": "--enable-features=CertVerifierBuiltinFeature"
}
]
},
"bundle": {
"active": true,
"publisher": "Stirling PDF Inc.",
"targets": ["deb", "rpm", "appimage", "dmg", "msi"],
"icon": [
"icons/icon.png",
"icons/icon.icns",
"icons/icon.ico",
"icons/16x16.png",
"icons/32x32.png",
"icons/64x64.png",
"icons/128x128.png",
"icons/192x192.png"
],
"resources": ["libs/*.jar", "runtime/jre/**/*"],
"fileAssociations": [
{
"ext": ["pdf"],
"name": "PDF Document",
"role": "Editor",
"mimeType": "application/pdf"
}
],
"linux": {
"deb": {
"desktopTemplate": "stirling-pdf.desktop"
},
"rpm": {
"desktopTemplate": "stirling-pdf.desktop"
},
"appimage": {
"bundleMediaFramework": false
}
},
"app": {
"windows": [
{
"title": "Stirling-PDF",
"width": 1280,
"height": 800,
"resizable": true,
"fullscreen": false,
"additionalBrowserArgs": "--enable-features=CertVerifierBuiltinFeature"
}
]
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com",
"wix": {
"fragmentPaths": ["windows/wix/provisioning.wxs"],
"componentGroupRefs": ["ProvisioningComponentGroup"]
}
},
"bundle": {
"active": true,
"publisher": "Stirling PDF Inc.",
"targets": [
"deb",
"rpm",
"appimage",
"dmg",
"msi"
],
"icon": [
"icons/icon.png",
"icons/icon.icns",
"icons/icon.ico",
"icons/16x16.png",
"icons/32x32.png",
"icons/64x64.png",
"icons/128x128.png",
"icons/192x192.png"
],
"resources": [
"libs/*.jar",
"runtime/jre/**/*"
],
"fileAssociations": [
{
"ext": [
"pdf"
],
"name": "PDF Document",
"role": "Editor",
"mimeType": "application/pdf"
}
],
"linux": {
"deb": {
"desktopTemplate": "stirling-pdf.desktop"
},
"rpm": {
"desktopTemplate": "stirling-pdf.desktop"
},
"appimage": {
"bundleMediaFramework": false
}
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com",
"wix": {
"fragmentPaths": [
"windows/wix/provisioning.wxs"
],
"componentGroupRefs": [
"ProvisioningComponentGroup"
]
}
},
"macOS": {
"minimumSystemVersion": "10.15",
"signingIdentity": null,
"entitlements": null,
"providerShortName": null,
"infoPlist": "Info.plist"
}
},
"plugins": {
"shell": {
"open": true
},
"fs": {
"requireLiteralLeadingDot": false
},
"deep-link": {
"desktop": {
"schemes": [
"stirlingpdf"
]
}
}
"macOS": {
"minimumSystemVersion": "10.15",
"signingIdentity": null,
"entitlements": null,
"providerShortName": null,
"infoPlist": "Info.plist"
}
},
"plugins": {
"shell": {
"open": true
},
"fs": {
"requireLiteralLeadingDot": false
},
"deep-link": {
"desktop": {
"schemes": ["stirlingpdf"]
}
}
}
}
@@ -22,6 +22,7 @@ import {
extractLatestFilesFromBundle,
parseContentDispositionFilename,
} from "@app/services/shareBundleUtils";
import { getHeaderValue } from "@app/services/httpHeaderUtils";
import { useTranslation } from "react-i18next";
import { openFilesFromDisk } from "@app/services/openFilesFromDisk";
export { pendingFilePathMappings } from "@app/services/pendingFilePathMappings";
@@ -893,16 +894,11 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
skipAuthRedirect: true,
} as any,
);
const contentType =
(response.headers &&
(response.headers["content-type"] ||
response.headers["Content-Type"])) ||
"";
const disposition =
(response.headers &&
(response.headers["content-disposition"] ||
response.headers["Content-Disposition"])) ||
"";
const contentType = getHeaderValue(response.headers, "content-type");
const disposition = getHeaderValue(
response.headers,
"content-disposition",
);
const filename =
parseContentDispositionFilename(disposition) ||
file.name ||
@@ -20,6 +20,7 @@ import {
loadShareBundleEntries,
parseContentDispositionFilename,
} from "@app/services/shareBundleUtils";
import { getHeaderValue } from "@app/services/httpHeaderUtils";
interface FilesModalContextType {
isFilesModalOpen: boolean;
@@ -173,16 +174,8 @@ export const FilesModalProvider: React.FC<{ children: React.ReactNode }> = ({
skipAuthRedirect: true,
} as any,
);
const contentType =
(response.headers &&
(response.headers["content-type"] ||
response.headers["Content-Type"])) ||
"";
const disposition =
(response.headers &&
(response.headers["content-disposition"] ||
response.headers["Content-Disposition"])) ||
"";
const contentType = getHeaderValue(response.headers, "content-type");
const disposition = getHeaderValue(response.headers, "content-disposition");
const filename =
parseContentDispositionFilename(disposition) || "server-file";
const blob = response.data as Blob;
@@ -199,16 +192,8 @@ export const FilesModalProvider: React.FC<{ children: React.ReactNode }> = ({
skipAuthRedirect: true,
} as any,
);
const contentType =
(response.headers &&
(response.headers["content-type"] ||
response.headers["Content-Type"])) ||
"";
const disposition =
(response.headers &&
(response.headers["content-disposition"] ||
response.headers["Content-Disposition"])) ||
"";
const contentType = getHeaderValue(response.headers, "content-type");
const disposition = getHeaderValue(response.headers, "content-disposition");
const filename =
parseContentDispositionFilename(disposition) || "shared-file";
const blob = response.data as Blob;
@@ -241,6 +226,11 @@ export const FilesModalProvider: React.FC<{ children: React.ReactNode }> = ({
// Use custom handler for special cases (like page insertion)
customHandler(files, insertAfterPage);
} else {
// Snapshot selection before upload; addFiles may replace the current
// selection with only newly uploaded files.
const previouslySelected = fileCtx.selectors
.getSelectedStirlingFileStubs()
.map((s) => s.id);
// 1) Add via standard flow (auto-selects new files)
await addFiles(files);
// 2) Merge all requested file IDs (covers already-present files too)
@@ -252,7 +242,7 @@ export const FilesModalProvider: React.FC<{ children: React.ReactNode }> = ({
.getSelectedStirlingFileStubs()
.map((s) => s.id);
const nextSelection = Array.from(
new Set([...currentSelected, ...ids]),
new Set([...previouslySelected, ...currentSelected, ...ids]),
);
actions.setSelectedFiles(nextSelection);
}
@@ -0,0 +1,61 @@
type HeaderPrimitive = string | number | boolean;
function normalizeHeaderValue(value: unknown): string | undefined {
if (typeof value === "string") {
return value;
}
if (typeof value === "number" || typeof value === "boolean") {
return String(value);
}
if (Array.isArray(value)) {
const stringValues = value.filter(
(entry): entry is HeaderPrimitive =>
typeof entry === "string" ||
typeof entry === "number" ||
typeof entry === "boolean",
);
return stringValues.length > 0
? stringValues.map(String).join(", ")
: undefined;
}
return undefined;
}
export function getHeaderValue(headers: unknown, headerName: string): string {
const normalizedName = headerName.toLowerCase();
if (!headers || typeof headers !== "object") {
return "";
}
const maybeHeaders = headers as Record<string, unknown> & {
get?: (name: string) => unknown;
};
const directValue =
maybeHeaders[headerName] ??
maybeHeaders[normalizedName] ??
maybeHeaders[headerName.toUpperCase()];
const normalizedDirect = normalizeHeaderValue(directValue);
if (normalizedDirect) {
return normalizedDirect;
}
if (typeof maybeHeaders.get === "function") {
const fromGetter = normalizeHeaderValue(maybeHeaders.get(headerName));
if (fromGetter) {
return fromGetter;
}
}
for (const [key, value] of Object.entries(maybeHeaders)) {
if (key.toLowerCase() === normalizedName) {
const normalized = normalizeHeaderValue(value);
if (normalized) {
return normalized;
}
}
}
return "";
}
@@ -1,4 +1,5 @@
import apiClient from "@app/services/apiClient";
import { getHeaderValue } from "@app/services/httpHeaderUtils";
import type { SavedSignature } from "@app/types/signature";
export type StorageType = "backend" | "localStorage";
@@ -166,7 +167,9 @@ class SignatureStorageService {
// Convert to data URL (base64) for both display and use
const blob = new Blob([imageResponse.data], {
type: imageResponse.headers["content-type"] || "image/png",
type:
getHeaderValue(imageResponse.headers, "content-type") ||
"image/png",
});
const dataUrl = await new Promise<string>((resolve, reject) => {
@@ -16,6 +16,7 @@ import {
loadShareBundleEntries,
parseContentDispositionFilename,
} from "@app/services/shareBundleUtils";
import { getHeaderValue } from "@app/services/httpHeaderUtils";
interface ShareLinkLoaderProps {
token: string;
@@ -78,16 +79,11 @@ export default function ShareLinkLoader({ token }: ShareLinkLoaderProps) {
);
if (signal.aborted) return;
const contentType =
(response.headers &&
(response.headers["content-type"] ||
response.headers["Content-Type"])) ||
"";
const disposition =
(response.headers &&
(response.headers["content-disposition"] ||
response.headers["Content-Disposition"])) ||
"";
const contentType = getHeaderValue(response.headers, "content-type");
const disposition = getHeaderValue(
response.headers,
"content-disposition",
);
const filename =
parseContentDispositionFilename(disposition) || "shared-file";
const blob = response.data as Blob;
@@ -9,6 +9,7 @@ import {
loadShareBundleEntries,
parseContentDispositionFilename,
} from "@app/services/shareBundleUtils";
import { getHeaderValue } from "@app/services/httpHeaderUtils";
export interface ShareLinkMetadata {
shareToken?: string;
@@ -44,15 +45,8 @@ export async function downloadShareLink(token: string): Promise<{
suppressErrorToast: true,
skipAuthRedirect: true,
});
const contentType =
(response.headers &&
(response.headers["content-type"] || response.headers["Content-Type"])) ||
"";
const disposition =
(response.headers &&
(response.headers["content-disposition"] ||
response.headers["Content-Disposition"])) ||
"";
const contentType = getHeaderValue(response.headers, "content-type");
const disposition = getHeaderValue(response.headers, "content-disposition");
const filename =
parseContentDispositionFilename(disposition) || "shared-file";
const blob = response.data as Blob;