+ {/* Subtle glowing ambient backdrop */}
+
+
+ {/* Error icon badge */}
+
+
+ {/* Title */}
+
+ {toolTitle ? `${toolTitle} encountered an error` : 'Something went wrong'}
+
+
+ {/* Subtitle / friendly message */}
+
+ An unexpected issue occurred while rendering this tool. The rest of Toolglass remains fully functional.
+
+
+ {/* Error message pill */}
+ {error && (
+
+ Error:
+ {error.message || 'Unknown runtime exception'}
+
+ )}
+
+ {/* Privacy assurance callout */}
+
+
+ 100% Client-Side Privacy: No error logs or data were sent over the network.
+
+
+ {/* Action buttons */}
+
+
{ e.currentTarget.style.opacity = '0.92'; }}
+ onMouseOut={(e) => { e.currentTarget.style.opacity = '1'; }}
+ >
+
+ Try Again
+
+
+
+
+ Return to Tools
+
+
+
+ {copied ? : }
+ {copied ? 'Copied' : 'Copy Debug Info'}
+
+
+
+ {/* Technical stack toggle */}
+ {errorInfo && (
+
+
this.setState((prev) => ({ showDetails: !prev.showDetails }))}
+ style={{
+ background: 'transparent',
+ border: 'none',
+ color: 'var(--ink-mute)',
+ fontSize: 12,
+ cursor: 'pointer',
+ display: 'flex',
+ alignItems: 'center',
+ gap: 6,
+ padding: '4px 0',
+ margin: '0 auto',
+ }}
+ >
+ {showDetails ? 'Hide technical stack' : 'View technical stack (client-only)'}
+
+
+ {showDetails && (
+
+ {errorInfo.componentStack?.trim() || 'No component stack recorded'}
+
+ )}
+
+ )}
+
+ );
+ }
+}
diff --git a/src/pages/NotFound.test.tsx b/src/pages/NotFound.test.tsx
new file mode 100644
index 0000000..01cb5aa
--- /dev/null
+++ b/src/pages/NotFound.test.tsx
@@ -0,0 +1,97 @@
+import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
+import { act } from 'react';
+import { createRoot, type Root } from 'react-dom/client';
+import { MemoryRouter } from 'react-router-dom';
+import { NotFound } from './NotFound';
+
+// Configure act environment for React 19
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
+
+describe('NotFound Page Component', () => {
+ let container: HTMLDivElement;
+ let root: Root;
+
+ beforeEach(() => {
+ container = document.createElement('div');
+ document.body.appendChild(container);
+ root = createRoot(container);
+ });
+
+ afterEach(() => {
+ act(() => {
+ root.unmount();
+ });
+ container.remove();
+ vi.restoreAllMocks();
+ });
+
+ it('renders general 404 page for unknown paths', () => {
+ act(() => {
+ root.render(
+
+ {/* 404 Hero Card */}
+
+ {/* Glowing atmospheric gradient */}
+
+
+ {/* 404 Visual Badge */}
+
+
+ 404 · ERROR
+
+
+ {/* Heading */}
+
+ {isTool ? (
+ <>
+ Tool Not Found:{' '}
+
+ {detectedSlug || 'Unknown'}
+
+ >
+ ) : (
+ 'Page Not Found'
+ )}
+
+
+ {/* Message */}
+
+ {isTool
+ ? `The requested tool "${detectedSlug}" does not exist or may have been renamed.`
+ : `The path "${cleanPath}" does not exist in Toolglass.`}
+
+
+ {/* Inline Search Bar */}
+
+
+
+ setSearchQuery(e.target.value)}
+ aria-label="Search tools directly"
+ style={{
+ width: '100%',
+ background: 'transparent',
+ border: 'none',
+ outline: 'none',
+ fontSize: 14,
+ color: 'var(--ink)',
+ fontFamily: 'var(--font-sans)',
+ }}
+ />
+ {searchQuery && (
+ setSearchQuery('')}
+ style={{
+ background: 'transparent',
+ border: 'none',
+ color: 'var(--ink-mute)',
+ cursor: 'pointer',
+ fontSize: 13,
+ padding: 2,
+ }}
+ >
+ ✕
+
+ )}
+
+
+
+ {/* Actions */}
+
+
+
+ Explore All Tools
+
+
+
+
+ {/* Search Results (if user is actively typing) */}
+ {searchQuery.trim() && (
+
+
+
+ Search Results for "{searchQuery}" ({searchResults.length})
+
+
+ {searchResults.length > 0 ? (
+
+ {searchResults.map((tool) => (
+
+ ))}
+
+ ) : (
+
+
+ No tools matching "{searchQuery}". Try browsing all tools on the home page.
+
+ )}
+
+ )}
+
+ {/* Suggested Tools Section (Shown when not actively searching) */}
+ {!searchQuery.trim() && suggestedTools.length > 0 && (
+
+
+
+
+ {detectedSlug ? 'Did you mean one of these tools?' : 'Popular Utilities'}
+
+
+ View all 17 tools →
+
+
+
+
+ {suggestedTools.map((tool) => (
+
+ ))}
+
+
+ )}
+
+ );
+}
+export default NotFound;
diff --git a/src/pages/ToolPage.tsx b/src/pages/ToolPage.tsx
index c1099df..ee4c907 100644
--- a/src/pages/ToolPage.tsx
+++ b/src/pages/ToolPage.tsx
@@ -1,8 +1,10 @@
import { AnimatePresence, motion } from 'framer-motion';
import { Suspense, useEffect, useState } from 'react';
-import { Navigate, useParams } from 'react-router-dom';
+import { useParams } from 'react-router-dom';
import { toolBySlug } from '../tools/registry';
import { useRecentTools } from '../hooks/useRecentTools';
+import { ErrorBoundary } from '../components/ErrorBoundary';
+import { NotFound } from './NotFound';
/* ── Cycling phrases shown while the tool chunk loads ────────────── */
const PHRASES = [
@@ -137,11 +139,16 @@ export function ToolPage() {
}
}, [tool, addRecent]);
- if (!tool) return