gbclient/app/paper/page.tsx

225 lines
8.7 KiB
TypeScript
Raw Normal View History

"use client";
import { useState, useRef, useEffect } from 'react';
import { useEditor, EditorContent, BubbleMenu, AnyExtension } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import TextStyle from '@tiptap/extension-text-style';
import Color from '@tiptap/extension-color';
import Highlight from '@tiptap/extension-highlight';
import TextAlign from '@tiptap/extension-text-align';
import Footer from '../footer'
import {
Bold, Italic, Underline, AlignLeft, AlignCenter, AlignRight,
Link, Highlighter, Type} from 'lucide-react';
const SimplePaperNote = () => {
const [isEditingTitle] = useState(false);
const titleInputRef = useRef(null);
const editor = useEditor({
extensions: [
StarterKit.extend() as unknown as AnyExtension,
TextStyle,
Color,
Highlight.configure({ multicolor: true }),
TextAlign.configure({
types: ['heading', 'paragraph'],
}),
],
content: `
<p>Start writing your thoughts here...</p>
`,
editorProps: {
attributes: {
class: 'prose prose-invert max-w-none focus:outline-none min-h-[calc(100vh-8rem)] p-8 text-foreground',
},
},
});
const addLink = () => {
const previousUrl = editor?.getAttributes('link').href;
const url = window.prompt('Enter URL:', previousUrl);
if (url === null) return;
if (url === '') {
editor?.chain().focus().extendMarkRange('link').unsetLink().run();
return;
}
editor?.chain().focus().extendMarkRange('link').setLink({ href: url }).run();
};
useEffect(() => {
if (isEditingTitle && titleInputRef.current) {
titleInputRef.current.focus();
}
}, [isEditingTitle]);
if (!editor) {
return null;
}
// OneNote-like keyboard shortcuts (note editing & navigation)
// Two rows, unique qkeys, avoid browser/reserved keys
// File/Note operations row
const shortcuts = [
[
{ key: 'Q', label: 'Resume', action: () => {/* Implement resume logic here */ } },
{ key: 'W', label: 'Write', action: () => {/* Implement write logic here */ } },
{ key: 'E', label: 'Expand', action: () => {/* Implement expand logic here */ } },
{ key: 'R', label: 'One Word', action: () => {/* Implement one word logic here */ } },
{ key: 'T', label: 'As List', action: () => {} },
{ key: 'Y', label: 'As Mail', action: () => {/* Implement as mail logic here */ } },
{ key: 'U', label: 'Copy', action: () => document.execCommand('copy') },
{ key: 'I', label: 'Paste', action: () => document.execCommand('paste') },
{ key: 'O', label: 'Undo', action: () => editor?.chain().focus().undo().run() },
{ key: 'P', label: 'Redo', action: () => editor?.chain().focus().redo().run() },
],
[
{ key: 'A', label: 'Select', action: () => {/* Implement select logic here */ } },
{ key: 'S', label: 'Select All', action: () => editor?.chain().focus().selectAll().run() },
{ key: 'D', label: 'Deselect', action: () => {/* Implement deselect logic here */ } },
{ key: 'G', label: 'Random', action: () => {/* Implement insert image logic here */ } },
{ key: 'H', label: 'Idea', action: () => {/* Implement insert table logic here */ } },
{ key: 'J', label: 'Insert Link', action: addLink },
{ key: 'K', label: 'Highlight', action: () => editor?.chain().focus().toggleHighlight({ color: '#ffff00' }).run() },
{ key: 'L', label: 'To-Do', action: () => {}},
{ key: 'Z', label: 'Zoom In', action: () => {/* Implement zoom in logic here */ } },
{ key: 'X', label: 'Zoom Out', action: () => {/* Implement zoom out logic here */ } },
]
];
return (
<div className="min-h-screen bg-background text-foreground">
<div>
<div className="max-w-4xl mx-auto">
{/* Paper Shadow Effect */}
<div className="mx-4 my-8 bg-card rounded-lg shadow-2xl shadow-black/20 border border-border">
<EditorContent
editor={editor}
className="min-h-[calc(100vh-12rem)]"
/>
</div>
</div>
{/* Floating Selection Toolbar */}
{editor && (
<BubbleMenu
editor={editor}
tippyOptions={{
duration: 100,
placement: 'top',
animation: 'shift-away'
}}
>
<div className="flex items-center bg-card border border-border rounded-lg shadow-lg p-1">
{/* Text Formatting */}
<button
onClick={() => editor.chain().focus().toggleBold().run()}
className={`p-2 rounded hover:bg-accent transition-colors ${editor.isActive('bold') ? 'bg-primary text-primary-foreground' : 'text-foreground'
}`}
title="Bold"
>
<Bold className="h-4 w-4" />
</button>
<button
onClick={() => editor.chain().focus().toggleItalic().run()}
className={`p-2 rounded hover:bg-accent transition-colors ${editor.isActive('italic') ? 'bg-primary text-primary-foreground' : 'text-foreground'
}`}
title="Italic"
>
<Italic className="h-4 w-4" />
</button>
<button
onClick={() => editor.chain().focus().toggleUnderline().run()}
className={`p-2 rounded hover:bg-accent transition-colors ${editor.isActive('underline') ? 'bg-primary text-primary-foreground' : 'text-foreground'
}`}
title="Underline"
>
<Underline className="h-4 w-4" />
</button>
<div className="w-px h-6 bg-border mx-1"></div>
{/* Text Alignment */}
<button
onClick={() => editor.chain().focus().setTextAlign('left').run()}
className={`p-2 rounded hover:bg-accent transition-colors ${editor.isActive({ textAlign: 'left' }) ? 'bg-primary text-primary-foreground' : 'text-foreground'
}`}
title="Align Left"
>
<AlignLeft className="h-4 w-4" />
</button>
<button
onClick={() => editor.chain().focus().setTextAlign('center').run()}
className={`p-2 rounded hover:bg-accent transition-colors ${editor.isActive({ textAlign: 'center' }) ? 'bg-primary text-primary-foreground' : 'text-foreground'
}`}
title="Align Center"
>
<AlignCenter className="h-4 w-4" />
</button>
<button
onClick={() => editor.chain().focus().setTextAlign('right').run()}
className={`p-2 rounded hover:bg-accent transition-colors ${editor.isActive({ textAlign: 'right' }) ? 'bg-primary text-primary-foreground' : 'text-foreground'
}`}
title="Align Right"
>
<AlignRight className="h-4 w-4" />
</button>
<div className="w-px h-6 bg-border mx-1"></div>
{/* Highlight */}
<button
onClick={() => editor.chain().focus().toggleHighlight({ color: '#ffff00' }).run()}
className={`p-2 rounded hover:bg-accent transition-colors ${editor.isActive('highlight') ? 'bg-primary text-primary-foreground' : 'text-foreground'
}`}
title="Highlight"
>
<Highlighter className="h-4 w-4" />
</button>
{/* Link */}
<button
onClick={addLink}
className={`p-2 rounded hover:bg-accent transition-colors ${editor.isActive('link') ? 'bg-primary text-primary-foreground' : 'text-foreground'
}`}
title="Add Link"
>
<Link className="h-4 w-4" />
</button>
<div className="w-px h-6 bg-border mx-1"></div>
{/* Heading */}
<button
onClick={() => {
if (editor.isActive('heading')) {
editor.chain().focus().setNode('paragraph').run();
} else {
editor.chain().focus().setNode('heading', { level: 2 }).run();
}
}}
className={`p-2 rounded hover:bg-accent transition-colors ${editor.isActive('heading') ? 'bg-primary text-primary-foreground' : 'text-foreground'
}`}
title="Heading"
>
<Type className="h-4 w-4" />
</button>
</div>
</BubbleMenu>
)}
</div>
<Footer shortcuts={shortcuts} />
</div>
);
};
export default SimplePaperNote;