mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 11:23:10 +02:00
Posthog, scarf and url navigation overhaul (#4318)
Added post hog project - always enabled Added scarf pixel - Always enabled Reworked Url navigation Forward and back now works without reloading page --------- Co-authored-by: Connor Yoh <[email protected]>
This commit is contained in:
@@ -31,7 +31,7 @@ export const executeToolOperationWithPrefix = async (
|
||||
): Promise<File[]> => {
|
||||
console.log(`🔧 Executing tool: ${operationName}`, { parameters, fileCount: files.length });
|
||||
|
||||
const config = toolRegistry[operationName]?.operationConfig;
|
||||
const config = toolRegistry[operationName as keyof ToolRegistry]?.operationConfig;
|
||||
if (!config) {
|
||||
console.error(`❌ Tool operation not supported: ${operationName}`);
|
||||
throw new Error(`Tool operation not supported: ${operationName}`);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
let lastFiredPathname: string | null = null;
|
||||
let lastFiredTime = 0;
|
||||
|
||||
/**
|
||||
* Fire scarf pixel for analytics tracking
|
||||
* Only fires if pathname is different from last call or enough time has passed
|
||||
*/
|
||||
export function firePixel(pathname: string): void {
|
||||
const now = Date.now();
|
||||
|
||||
// Only fire if pathname changed or it's been at least 1 second since last fire
|
||||
if (pathname === lastFiredPathname && now - lastFiredTime < 250) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastFiredPathname = pathname;
|
||||
lastFiredTime = now;
|
||||
|
||||
const url = 'https://static.scarf.sh/a.png?x-pxid=3c1d68de-8945-4e9f-873f-65320b6fabf7'
|
||||
+ '&path=' + encodeURIComponent(pathname)
|
||||
|
||||
const img = new Image();
|
||||
img.referrerPolicy = "no-referrer-when-downgrade";
|
||||
img.src = url;
|
||||
|
||||
console.log("ScarfPixel: Fire to... " + pathname);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { ToolId } from '../types/toolId';
|
||||
|
||||
// Map URL paths to tool keys (multiple URLs can map to same tool)
|
||||
export const URL_TO_TOOL_MAP: Record<string, ToolId> = {
|
||||
'/split-pdfs': 'split',
|
||||
'/split': 'split',
|
||||
'/merge-pdfs': 'mergePdfs',
|
||||
'/compress-pdf': 'compress',
|
||||
'/convert': 'convert',
|
||||
'/convert-pdf': 'convert',
|
||||
'/file-to-pdf': 'convert',
|
||||
'/eml-to-pdf': 'convert',
|
||||
'/html-to-pdf': 'convert',
|
||||
'/markdown-to-pdf': 'convert',
|
||||
'/pdf-to-csv': 'convert',
|
||||
'/pdf-to-img': 'convert',
|
||||
'/pdf-to-markdown': 'convert',
|
||||
'/pdf-to-pdfa': 'convert',
|
||||
'/pdf-to-word': 'convert',
|
||||
'/pdf-to-xml': 'convert',
|
||||
'/add-password': 'addPassword',
|
||||
'/change-permissions': 'change-permissions',
|
||||
'/sanitize-pdf': 'sanitize',
|
||||
'/ocr': 'ocr',
|
||||
'/ocr-pdf': 'ocr',
|
||||
'/add-watermark': 'addWatermark',
|
||||
'/remove-password': 'remove-password',
|
||||
'/single-large-page': 'single-large-page',
|
||||
'/repair': 'repair',
|
||||
'/unlock-pdf-forms': 'unlock-pdf-forms',
|
||||
'/remove-certificate-sign': 'remove-certificate-sign',
|
||||
'/remove-cert-sign': 'remove-certificate-sign'
|
||||
};
|
||||
@@ -1,167 +1,127 @@
|
||||
/**
|
||||
* URL routing utilities for tool navigation
|
||||
* Provides clean URL routing for the V2 tool system
|
||||
* URL routing utilities for tool navigation with registry support
|
||||
*/
|
||||
|
||||
import { ModeType, isValidMode as isValidModeType, getDefaultMode, ToolRoute } from '../types/navigation';
|
||||
import { ToolRoute } from '../types/navigation';
|
||||
import { ToolId, isValidToolId } from '../types/toolId';
|
||||
import { getDefaultWorkbench } from '../types/workbench';
|
||||
import { ToolRegistry, getToolWorkbench, getToolUrlPath } from '../data/toolsTaxonomy';
|
||||
import { firePixel } from './scarfTracking';
|
||||
import { URL_TO_TOOL_MAP } from './urlMapping';
|
||||
|
||||
/**
|
||||
* Parse the current URL to extract tool routing information
|
||||
*/
|
||||
export function parseToolRoute(): ToolRoute {
|
||||
export function parseToolRoute(registry: ToolRegistry): ToolRoute {
|
||||
const path = window.location.pathname;
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
|
||||
// Extract tool from URL path (e.g., /split-pdf -> split)
|
||||
const toolMatch = path.match(/\/([a-zA-Z-]+)(?:-pdf)?$/);
|
||||
if (toolMatch) {
|
||||
const toolKey = toolMatch[1].toLowerCase();
|
||||
|
||||
// Map URL paths to tool keys and modes (excluding internal UI modes)
|
||||
const toolMappings: Record<string, { mode: ModeType; toolKey: string }> = {
|
||||
'split': { mode: 'split', toolKey: 'split' },
|
||||
'merge': { mode: 'merge', toolKey: 'merge' },
|
||||
'compress': { mode: 'compress', toolKey: 'compress' },
|
||||
'convert': { mode: 'convert', toolKey: 'convert' },
|
||||
'add-password': { mode: 'addPassword', toolKey: 'addPassword' },
|
||||
'change-permissions': { mode: 'changePermissions', toolKey: 'changePermissions' },
|
||||
'sanitize': { mode: 'sanitize', toolKey: 'sanitize' },
|
||||
'ocr': { mode: 'ocr', toolKey: 'ocr' }
|
||||
|
||||
// First, check URL mapping for multiple URL aliases
|
||||
const mappedToolId = URL_TO_TOOL_MAP[path];
|
||||
if (mappedToolId && registry[mappedToolId]) {
|
||||
const tool = registry[mappedToolId];
|
||||
return {
|
||||
workbench: getToolWorkbench(tool),
|
||||
toolId: mappedToolId
|
||||
};
|
||||
|
||||
const mapping = toolMappings[toolKey];
|
||||
if (mapping) {
|
||||
}
|
||||
|
||||
// Fallback: Try to find tool by primary URL path in registry
|
||||
for (const [toolId, tool] of Object.entries(registry)) {
|
||||
const toolUrlPath = getToolUrlPath(toolId, tool);
|
||||
if (path === toolUrlPath && isValidToolId(toolId)) {
|
||||
return {
|
||||
mode: mapping.mode,
|
||||
toolKey: mapping.toolKey
|
||||
workbench: getToolWorkbench(tool),
|
||||
toolId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check for query parameter fallback (e.g., ?tool=split)
|
||||
const toolParam = searchParams.get('tool');
|
||||
if (toolParam && isValidModeType(toolParam)) {
|
||||
if (toolParam && isValidToolId(toolParam) && registry[toolParam]) {
|
||||
const tool = registry[toolParam];
|
||||
return {
|
||||
mode: toolParam as ModeType,
|
||||
toolKey: toolParam
|
||||
workbench: getToolWorkbench(tool),
|
||||
toolId: toolParam
|
||||
};
|
||||
}
|
||||
|
||||
// Default to page editor for home page
|
||||
|
||||
// Default to fileEditor workbench for home page
|
||||
return {
|
||||
mode: getDefaultMode(),
|
||||
toolKey: null
|
||||
workbench: getDefaultWorkbench(),
|
||||
toolId: null
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the URL to reflect the current tool selection
|
||||
* Internal UI modes (viewer, fileEditor, pageEditor) don't get URLs
|
||||
* Update URL and fire analytics pixel
|
||||
*/
|
||||
export function updateToolRoute(mode: ModeType, toolKey?: string): void {
|
||||
function updateUrl(newPath: string, searchParams: URLSearchParams, replace: boolean = false): void {
|
||||
const currentPath = window.location.pathname;
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
|
||||
// Don't create URLs for internal UI modes
|
||||
if (mode === 'viewer' || mode === 'fileEditor' || mode === 'pageEditor') {
|
||||
// If we're switching to an internal mode, clear any existing tool URL
|
||||
if (currentPath !== '/') {
|
||||
clearToolRoute();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let newPath = '/';
|
||||
|
||||
// Map modes to URL paths (only for actual tools)
|
||||
if (toolKey) {
|
||||
const pathMappings: Record<string, string> = {
|
||||
'split': '/split-pdf',
|
||||
'merge': '/merge-pdf',
|
||||
'compress': '/compress-pdf',
|
||||
'convert': '/convert-pdf',
|
||||
'addPassword': '/add-password-pdf',
|
||||
'changePermissions': '/change-permissions-pdf',
|
||||
'sanitize': '/sanitize-pdf',
|
||||
'ocr': '/ocr-pdf'
|
||||
};
|
||||
|
||||
newPath = pathMappings[toolKey] || `/${toolKey}`;
|
||||
}
|
||||
|
||||
// Remove tool query parameter since we're using path-based routing
|
||||
searchParams.delete('tool');
|
||||
|
||||
// Construct final URL
|
||||
const queryString = searchParams.toString();
|
||||
const fullUrl = newPath + (queryString ? `?${queryString}` : '');
|
||||
|
||||
// Update URL without triggering page reload
|
||||
|
||||
// Only update URL and fire pixel if something actually changed
|
||||
if (currentPath !== newPath || window.location.search !== (queryString ? `?${queryString}` : '')) {
|
||||
window.history.replaceState(null, '', fullUrl);
|
||||
if (replace) {
|
||||
window.history.replaceState(null, '', fullUrl);
|
||||
} else {
|
||||
window.history.pushState(null, '', fullUrl);
|
||||
}
|
||||
firePixel(newPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the URL to reflect the current tool selection
|
||||
*/
|
||||
export function updateToolRoute(toolId: ToolId, registry: ToolRegistry, replace: boolean = false): void {
|
||||
const tool = registry[toolId];
|
||||
if (!tool) {
|
||||
console.warn(`Tool ${toolId} not found in registry`);
|
||||
return;
|
||||
}
|
||||
|
||||
const newPath = getToolUrlPath(toolId, tool);
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
|
||||
// Remove tool query parameter since we're using path-based routing
|
||||
searchParams.delete('tool');
|
||||
|
||||
updateUrl(newPath, searchParams, replace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear tool routing and return to home page
|
||||
*/
|
||||
export function clearToolRoute(): void {
|
||||
export function clearToolRoute(replace: boolean = false): void {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.delete('tool');
|
||||
|
||||
const queryString = searchParams.toString();
|
||||
const url = '/' + (queryString ? `?${queryString}` : '');
|
||||
|
||||
window.history.replaceState(null, '', url);
|
||||
|
||||
updateUrl('/', searchParams, replace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get clean tool name for display purposes
|
||||
* Get clean tool name for display purposes using registry
|
||||
*/
|
||||
export function getToolDisplayName(toolKey: string): string {
|
||||
const displayNames: Record<string, string> = {
|
||||
'split': 'Split PDF',
|
||||
'merge': 'Merge PDF',
|
||||
'compress': 'Compress PDF',
|
||||
'convert': 'Convert PDF',
|
||||
'addPassword': 'Add Password',
|
||||
'changePermissions': 'Change Permissions',
|
||||
'sanitize': 'Sanitize PDF',
|
||||
'ocr': 'OCR PDF'
|
||||
};
|
||||
|
||||
return displayNames[toolKey] || toolKey;
|
||||
export function getToolDisplayName(toolId: ToolId, registry: ToolRegistry): string {
|
||||
const tool = registry[toolId];
|
||||
return tool ? tool.name : toolId;
|
||||
}
|
||||
|
||||
// Note: isValidMode is now imported from types/navigation.ts
|
||||
|
||||
/**
|
||||
* Generate shareable URL for current tool state
|
||||
* Only generates URLs for actual tools, not internal UI modes
|
||||
* Generate shareable URL for current tool state using registry
|
||||
*/
|
||||
export function generateShareableUrl(mode: ModeType, toolKey?: string): string {
|
||||
export function generateShareableUrl(toolId: ToolId | null, registry: ToolRegistry): string {
|
||||
const baseUrl = window.location.origin;
|
||||
|
||||
// Don't generate URLs for internal UI modes
|
||||
if (mode === 'viewer' || mode === 'fileEditor' || mode === 'pageEditor') {
|
||||
|
||||
if (!toolId || !registry[toolId]) {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
if (toolKey) {
|
||||
const pathMappings: Record<string, string> = {
|
||||
'split': '/split-pdf',
|
||||
'merge': '/merge-pdf',
|
||||
'compress': '/compress-pdf',
|
||||
'convert': '/convert-pdf',
|
||||
'addPassword': '/add-password-pdf',
|
||||
'changePermissions': '/change-permissions-pdf',
|
||||
'sanitize': '/sanitize-pdf',
|
||||
'ocr': '/ocr-pdf'
|
||||
};
|
||||
|
||||
const path = pathMappings[toolKey] || `/${toolKey}`;
|
||||
return `${baseUrl}${path}`;
|
||||
}
|
||||
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
const tool = registry[toolId];
|
||||
|
||||
const path = getToolUrlPath(toolId, tool);
|
||||
return `${baseUrl}${path}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user