diff --git a/app/layout.tsx b/app/layout.tsx
index b0b3e38..bbad70a 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,5 +1,6 @@
import './globals.css';
import { DimensionIconsProvider } from '@/context/DimensionIconsContext';
+import ExternalNavigationManager from '@/components/system/ExternalNavigationManager';
export const metadata = {
title: 'RA-H Open Source',
@@ -15,6 +16,7 @@ export default function RootLayout({
{
+ if (!href || !shouldOpenExternally(href)) {
+ return;
+ }
+
+ event.preventDefault();
+ void openExternalUrl(href).catch((error) => {
+ console.error('[MarkdownWithNodeTokens] Failed to open external link', error);
+ window.alert(`Unable to open ${href}`);
+ });
+ }}
style={{ color: '#22c55e', textDecoration: 'underline' }}
>
{processChildren(children, 'a')}
diff --git a/src/components/onboarding/FirstRunModal.tsx b/src/components/onboarding/FirstRunModal.tsx
index aa08dd8..82b4c5d 100644
--- a/src/components/onboarding/FirstRunModal.tsx
+++ b/src/components/onboarding/FirstRunModal.tsx
@@ -3,6 +3,7 @@
import { useState, useEffect, type CSSProperties } from 'react';
import { createPortal } from 'react-dom';
import { isFirstRun, markFirstRunComplete } from '@/services/storage/apiKeys';
+import { openExternalUrl } from '@/utils/openExternalUrl';
export default function FirstRunModal() {
const [isOpen, setIsOpen] = useState(false);
@@ -46,14 +47,18 @@ export default function FirstRunModal() {
@@ -148,4 +153,9 @@ const noteStyle: CSSProperties = {
const linkStyle: CSSProperties = {
color: '#22c55e',
textDecoration: 'none',
+ background: 'transparent',
+ border: 'none',
+ padding: 0,
+ cursor: 'pointer',
+ font: 'inherit',
};
diff --git a/src/components/settings/ApiKeysViewer.tsx b/src/components/settings/ApiKeysViewer.tsx
index c0bb966..0ac93b3 100644
--- a/src/components/settings/ApiKeysViewer.tsx
+++ b/src/components/settings/ApiKeysViewer.tsx
@@ -1,6 +1,7 @@
"use client";
import { useState, useEffect, type CSSProperties } from 'react';
+import { openExternalUrl } from '@/utils/openExternalUrl';
export default function ApiKeysViewer() {
const [status, setStatus] = useState<'checking' | 'configured' | 'not-set'>('checking');
@@ -59,14 +60,18 @@ export default function ApiKeysViewer() {
{/* Get key link */}
);
@@ -163,4 +168,9 @@ const helpStyle: CSSProperties = {
const linkStyle: CSSProperties = {
color: '#22c55e',
textDecoration: 'none',
+ background: 'transparent',
+ border: 'none',
+ padding: 0,
+ cursor: 'pointer',
+ font: 'inherit',
};
diff --git a/src/components/settings/DatabaseViewer.tsx b/src/components/settings/DatabaseViewer.tsx
index d9742a0..e45fb88 100644
--- a/src/components/settings/DatabaseViewer.tsx
+++ b/src/components/settings/DatabaseViewer.tsx
@@ -3,6 +3,7 @@
import { useCallback, useEffect, useMemo, useState, type ChangeEvent, type KeyboardEvent } from 'react';
import { Folder, Link as LinkIcon } from 'lucide-react';
import { Node } from '@/types/database';
+import { openExternalUrl } from '@/utils/openExternalUrl';
interface NodeWithMetrics extends Node {
edge_count?: number;
@@ -440,7 +441,12 @@ export default function DatabaseViewer() {
{node.title || 'Untitled node'}
{node.link && (