Spaces:
Sleeping
Sleeping
bigwolfe
commited on
Commit
·
7f96410
1
Parent(s):
044ec7f
wikilinks and graph working
Browse files
frontend/src/components/NoteViewer.tsx
CHANGED
|
@@ -35,6 +35,21 @@ export function NoteViewer({
|
|
| 35 |
[onWikilinkClick]
|
| 36 |
);
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
const formatDate = (dateString: string) => {
|
| 39 |
return new Date(dateString).toLocaleDateString('en-US', {
|
| 40 |
year: 'numeric',
|
|
@@ -72,12 +87,17 @@ export function NoteViewer({
|
|
| 72 |
|
| 73 |
{/* Content */}
|
| 74 |
<ScrollArea className="flex-1 p-6">
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
<div className="prose prose-slate dark:prose-invert max-w-none">
|
| 76 |
<ReactMarkdown
|
| 77 |
remarkPlugins={[remarkGfm]}
|
| 78 |
components={markdownComponents}
|
|
|
|
| 79 |
>
|
| 80 |
-
{
|
| 81 |
</ReactMarkdown>
|
| 82 |
</div>
|
| 83 |
|
|
|
|
| 35 |
[onWikilinkClick]
|
| 36 |
);
|
| 37 |
|
| 38 |
+
// Pre-process markdown to convert wikilinks to standard links
|
| 39 |
+
// [[Link]] -> [Link](wikilink:Link)
|
| 40 |
+
// [[Link|Alias]] -> [Alias](wikilink:Link)
|
| 41 |
+
const processedBody = useMemo(() => {
|
| 42 |
+
if (!note.body) return '';
|
| 43 |
+
const processed = note.body.replace(/\[\[([^\]]+)\]\]/g, (match, content) => {
|
| 44 |
+
const [link, alias] = content.split('|');
|
| 45 |
+
const displayText = alias || link;
|
| 46 |
+
const href = `wikilink:${encodeURIComponent(link)}`;
|
| 47 |
+
return `[${displayText}](${href})`;
|
| 48 |
+
});
|
| 49 |
+
// console.log('Processed Body:', processed);
|
| 50 |
+
return processed;
|
| 51 |
+
}, [note.body]);
|
| 52 |
+
|
| 53 |
const formatDate = (dateString: string) => {
|
| 54 |
return new Date(dateString).toLocaleDateString('en-US', {
|
| 55 |
year: 'numeric',
|
|
|
|
| 87 |
|
| 88 |
{/* Content */}
|
| 89 |
<ScrollArea className="flex-1 p-6">
|
| 90 |
+
{/* Debug: Verify processed output */}
|
| 91 |
+
{/* <pre className="text-xs text-muted-foreground mb-4 whitespace-pre-wrap bg-muted p-2 rounded">
|
| 92 |
+
{processedBody}
|
| 93 |
+
</pre> */}
|
| 94 |
<div className="prose prose-slate dark:prose-invert max-w-none">
|
| 95 |
<ReactMarkdown
|
| 96 |
remarkPlugins={[remarkGfm]}
|
| 97 |
components={markdownComponents}
|
| 98 |
+
urlTransform={(url) => url} // Allow all protocols including wikilink:
|
| 99 |
>
|
| 100 |
+
{processedBody}
|
| 101 |
</ReactMarkdown>
|
| 102 |
</div>
|
| 103 |
|
frontend/src/lib/markdown.tsx
CHANGED
|
@@ -17,27 +17,13 @@ export function createWikilinkComponent(
|
|
| 17 |
onWikilinkClick?: (linkText: string) => void
|
| 18 |
): Components {
|
| 19 |
return {
|
| 20 |
-
//
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
let lastIndex = 0;
|
| 26 |
-
let match;
|
| 27 |
-
let key = 0;
|
| 28 |
-
|
| 29 |
-
while ((match = pattern.exec(value)) !== null) {
|
| 30 |
-
// Add text before the wikilink
|
| 31 |
-
if (match.index > lastIndex) {
|
| 32 |
-
parts.push(value.slice(lastIndex, match.index));
|
| 33 |
-
}
|
| 34 |
-
|
| 35 |
-
// Add the wikilink as a clickable element
|
| 36 |
-
const linkText = match[1];
|
| 37 |
-
parts.push(
|
| 38 |
<span
|
| 39 |
-
|
| 40 |
-
className="wikilink cursor-pointer text-primary hover:underline"
|
| 41 |
onClick={(e) => {
|
| 42 |
e.preventDefault();
|
| 43 |
onWikilinkClick?.(linkText);
|
|
@@ -50,47 +36,13 @@ export function createWikilinkComponent(
|
|
| 50 |
onWikilinkClick?.(linkText);
|
| 51 |
}
|
| 52 |
}}
|
|
|
|
| 53 |
>
|
| 54 |
-
[[{linkText}]]
|
| 55 |
-
</span>
|
| 56 |
-
);
|
| 57 |
-
|
| 58 |
-
lastIndex = pattern.lastIndex;
|
| 59 |
-
}
|
| 60 |
-
|
| 61 |
-
// Add remaining text
|
| 62 |
-
if (lastIndex < value.length) {
|
| 63 |
-
parts.push(value.slice(lastIndex));
|
| 64 |
-
}
|
| 65 |
-
|
| 66 |
-
return parts.length > 0 ? <>{parts}</> : value;
|
| 67 |
-
},
|
| 68 |
-
|
| 69 |
-
// Style code blocks
|
| 70 |
-
code: ({ className, children, ...props }) => {
|
| 71 |
-
const match = /language-(\w+)/.exec(className || '');
|
| 72 |
-
const isInline = !match;
|
| 73 |
-
|
| 74 |
-
if (isInline) {
|
| 75 |
-
return (
|
| 76 |
-
<code className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono" {...props}>
|
| 77 |
{children}
|
| 78 |
-
</
|
| 79 |
);
|
| 80 |
}
|
| 81 |
|
| 82 |
-
return (
|
| 83 |
-
<code
|
| 84 |
-
className={`${className} block bg-muted p-4 rounded-lg overflow-x-auto text-sm font-mono`}
|
| 85 |
-
{...props}
|
| 86 |
-
>
|
| 87 |
-
{children}
|
| 88 |
-
</code>
|
| 89 |
-
);
|
| 90 |
-
},
|
| 91 |
-
|
| 92 |
-
// Style links
|
| 93 |
-
a: ({ href, children, ...props }) => {
|
| 94 |
const isExternal = href?.startsWith('http');
|
| 95 |
return (
|
| 96 |
<a
|
|
|
|
| 17 |
onWikilinkClick?: (linkText: string) => void
|
| 18 |
): Components {
|
| 19 |
return {
|
| 20 |
+
// Style links
|
| 21 |
+
a: ({ href, children, ...props }) => {
|
| 22 |
+
if (href?.startsWith('wikilink:')) {
|
| 23 |
+
const linkText = decodeURIComponent(href.replace('wikilink:', ''));
|
| 24 |
+
return (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
<span
|
| 26 |
+
className="wikilink cursor-pointer text-primary hover:underline font-medium text-blue-500 dark:text-blue-400"
|
|
|
|
| 27 |
onClick={(e) => {
|
| 28 |
e.preventDefault();
|
| 29 |
onWikilinkClick?.(linkText);
|
|
|
|
| 36 |
onWikilinkClick?.(linkText);
|
| 37 |
}
|
| 38 |
}}
|
| 39 |
+
title={`Go to ${linkText}`}
|
| 40 |
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
{children}
|
| 42 |
+
</span>
|
| 43 |
);
|
| 44 |
}
|
| 45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
const isExternal = href?.startsWith('http');
|
| 47 |
return (
|
| 48 |
<a
|
frontend/src/pages/MainApp.tsx
CHANGED
|
@@ -126,6 +126,7 @@ export function MainApp() {
|
|
| 126 |
// Handle wikilink clicks
|
| 127 |
const handleWikilinkClick = async (linkText: string) => {
|
| 128 |
const slug = normalizeSlug(linkText);
|
|
|
|
| 129 |
|
| 130 |
// Try to find exact match first
|
| 131 |
let targetNote = notes.find(
|
|
@@ -136,11 +137,13 @@ export function MainApp() {
|
|
| 136 |
if (!targetNote) {
|
| 137 |
targetNote = notes.find((note) => {
|
| 138 |
const pathSlug = normalizeSlug(note.note_path.replace(/\.md$/, ''));
|
|
|
|
| 139 |
return pathSlug.endsWith(slug);
|
| 140 |
});
|
| 141 |
}
|
| 142 |
|
| 143 |
if (targetNote) {
|
|
|
|
| 144 |
setSelectedPath(targetNote.note_path);
|
| 145 |
} else {
|
| 146 |
// TODO: Show "Create note" dialog
|
|
|
|
| 126 |
// Handle wikilink clicks
|
| 127 |
const handleWikilinkClick = async (linkText: string) => {
|
| 128 |
const slug = normalizeSlug(linkText);
|
| 129 |
+
console.log(`[Wikilink] Clicked: "${linkText}", Slug: "${slug}"`);
|
| 130 |
|
| 131 |
// Try to find exact match first
|
| 132 |
let targetNote = notes.find(
|
|
|
|
| 137 |
if (!targetNote) {
|
| 138 |
targetNote = notes.find((note) => {
|
| 139 |
const pathSlug = normalizeSlug(note.note_path.replace(/\.md$/, ''));
|
| 140 |
+
// console.log(`Checking path: ${note.note_path}, Slug: ${pathSlug}`);
|
| 141 |
return pathSlug.endsWith(slug);
|
| 142 |
});
|
| 143 |
}
|
| 144 |
|
| 145 |
if (targetNote) {
|
| 146 |
+
console.log(`[Wikilink] Found target: ${targetNote.note_path}`);
|
| 147 |
setSelectedPath(targetNote.note_path);
|
| 148 |
} else {
|
| 149 |
// TODO: Show "Create note" dialog
|