Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/editor/extensions/SlashCommands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
Image,
} from 'lucide-react';

interface CommandItem {
export interface CommandItem {
title: string;
command: ({ editor, range }: { editor: Editor; range: Range }) => void;
icon: React.ReactNode;
Expand Down
3 changes: 0 additions & 3 deletions src/components/editor/extensions/TableOfContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,8 @@ const TableOfContentsComponent = ({ editor, deleteNode }: TableOfContentsCompone
editor.state.doc.descendants((node: any, pos: number) => { // eslint-disable-line @typescript-eslint/no-explicit-any
if (node.type.name === 'heading') {
if (currentIndex === index) {
// Focus the editor and set cursor position
editor.commands.focus();
editor.commands.setTextSelection(pos + 1);

// Scroll to the heading
setTimeout(() => {
const element = editor.view.domAtPos(pos + 1);
if (element && element.node) {
Expand Down
116 changes: 84 additions & 32 deletions src/components/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
FolderInput,
Paperclip,
Printer,
EyeOff,
Eye,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
Expand All @@ -16,8 +18,9 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useEditor, EditorContent } from '@tiptap/react';
import { useEditor, EditorContent, Editor } from '@tiptap/react';
import ReactDOM from 'react-dom/client';
import type { Root } from 'react-dom/client';
import tippy from 'tippy.js';
import { createEditorExtensions } from './config/editor-config';
import { SlashCommands } from './extensions/SlashCommandsExtension';
Expand All @@ -33,13 +36,38 @@ import { useEditorEffects } from '@/components/editor/hooks/useEditorEffects';
import { fileService } from '@/services/fileService';
import type { Note, Folder as FolderType, FileAttachment } from '@/types/note';

// Type definitions for TipTap Suggestion
// Import CommandItem type from SlashCommands
import type { CommandItem } from './extensions/SlashCommands';

interface SuggestionProps {
editor: Editor;
range: { from: number; to: number };
query: string;
text: string;
command: (item: CommandItem) => void;
clientRect?: () => DOMRect | null;
decorationNode?: Element | null;
virtualNode?: Element | null;
}

interface SuggestionKeyDownProps {
event: KeyboardEvent;
}

interface SlashCommandsHandle {
onKeyDown: (props: SuggestionKeyDownProps) => boolean;
}

interface NoteEditorProps {
note: Note | null;
folders?: FolderType[];
onUpdateNote: (noteId: string, updates: Partial<Note>) => void;
onDeleteNote: (noteId: string) => void;
onArchiveNote: (noteId: string) => void;
onToggleStar: (noteId: string) => void;
onHideNote: (noteId: string) => void;
onUnhideNote: (noteId: string) => void;
userId?: string;
}

Expand All @@ -50,6 +78,8 @@ export default function Index({
onDeleteNote,
onArchiveNote,
onToggleStar,
onHideNote,
onUnhideNote,
userId = 'current-user',
}: NoteEditorProps) {
const [isMoveModalOpen, setIsMoveModalOpen] = useState(false);
Expand Down Expand Up @@ -80,6 +110,7 @@ export default function Index({

const editor = useEditor(
{
editable: !note?.hidden,
extensions: [
...createEditorExtensions(),
SlashCommands.configure({
Expand All @@ -88,23 +119,20 @@ export default function Index({
return [];
},
render: () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let component: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let popup: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let root: any;
let component: SlashCommandsHandle | null = null;
let popup: ReturnType<typeof tippy>[0] | null = null;
let root: Root | null = null;

return {
onStart: (props: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
onStart: (props: SuggestionProps) => {
if (!props.clientRect) {
return;
}

const container = document.createElement('div');

popup = tippy('body', {
getReferenceClientRect: props.clientRect,
getReferenceClientRect: props.clientRect as () => DOMRect,
appendTo: () => document.body,
content: container,
showOnCreate: true,
Expand All @@ -115,35 +143,23 @@ export default function Index({

root = ReactDOM.createRoot(container);

const commandsList = document.createElement('div');
component = ReactDOM.createRoot(commandsList);

component.render(
<SlashCommandsList
ref={(ref: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
component = ref;
}}
command={props.command}
/>
);

root.render(
<SlashCommandsList
ref={(ref: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
ref={(ref: SlashCommandsHandle | null) => {
component = ref;
}}
command={props.command}
/>
);
},

onUpdate: (props: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
onUpdate: (props: SuggestionProps) => {
popup?.setProps({
getReferenceClientRect: props.clientRect,
getReferenceClientRect: props.clientRect as () => DOMRect,
});
},

onKeyDown: (props: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
onKeyDown: (props: SuggestionKeyDownProps) => {
if (props.event.key === 'Escape') {
popup?.hide();
return true;
Expand All @@ -161,7 +177,7 @@ export default function Index({
},
}),
],
content: note?.content || '',
content: note?.hidden ? '[HIDDEN]' : (note?.content || ''),
editorProps: {
attributes: {
class:
Expand Down Expand Up @@ -246,7 +262,7 @@ export default function Index({
},
},
onUpdate: ({ editor }) => {
if (!note) return;
if (!note || note.hidden) return; // Prevent auto-save for hidden notes

// Update word and character counts
const text = editor.state.doc.textContent;
Expand Down Expand Up @@ -275,7 +291,7 @@ export default function Index({
}
},
},
[note?.id, updateCounts]
[note?.id, note?.hidden, updateCounts]
);

// Use custom hook for editor effects
Expand All @@ -292,6 +308,22 @@ export default function Index({



// Update editor when note hidden state changes
useEffect(() => {
if (editor && note) {
const currentContent = editor.getHTML();

// Only update if content actually changed
if (note.hidden && currentContent !== '[HIDDEN]') {
editor.commands.setContent('[HIDDEN]');
editor.setEditable(false);
} else if (!note.hidden && currentContent === '[HIDDEN]') {
editor.commands.setContent(note.content || '');
editor.setEditable(true);
}
}
}, [editor, note]);

// Cleanup timeout on unmount
useEffect(() => {
return () => {
Expand Down Expand Up @@ -476,6 +508,7 @@ export default function Index({
onChange={handleTitleChange}
className="text-foreground placeholder-muted-foreground min-w-0 flex-1 border-none bg-transparent text-2xl font-bold outline-none"
placeholder="Untitled Note"
disabled={note.hidden}
/>
</div>

Expand Down Expand Up @@ -508,12 +541,29 @@ export default function Index({
className={
note.starred ? 'text-yellow-500' : 'text-muted-foreground'
}
title={note.starred ? 'Remove from starred' : 'Add to starred'}
>
<Star
className={`h-4 w-4 ${note.starred ? 'fill-current' : ''}`}
/>
</Button>

<Button
variant="ghost"
size="sm"
onClick={() => note.hidden ? onUnhideNote(note.id) : onHideNote(note.id)}
className={
note.hidden ? 'text-primary' : 'text-muted-foreground'
}
title={note.hidden ? 'Unhide note' : 'Hide note'}
>
{note.hidden ? (
<EyeOff className="h-4 w-4" />
) : (
<Eye className="h-4 w-4" />
)}
</Button>

<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm">
Expand All @@ -523,7 +573,7 @@ export default function Index({
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => onToggleStar(note.id)}>
<Star className="mr-2 h-4 w-4" />
{note.starred ? 'Remove from Starred' : 'Star'}
{note.starred ? 'Unstar' : 'Star'}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setIsMoveModalOpen(true)}>
<FolderInput className="mr-2 h-4 w-4" />
Expand Down Expand Up @@ -585,9 +635,11 @@ export default function Index({
</div>
</div>

<div className="flex-shrink-0">
<Toolbar editor={editor} />
</div>
{!note.hidden && (
<div className="flex-shrink-0">
<Toolbar editor={editor} />
</div>
)}

{showAttachments && (
<>
Expand Down
5 changes: 4 additions & 1 deletion src/components/folders/QuickAccessSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface QuickAccessSectionProps {
starredCount: number;
archivedCount: number;
trashedCount: number;
hiddenCount: number;
onViewChange: (view: ViewMode) => void;
onFolderSelect: (folder: Folder | null) => void;
}
Expand All @@ -22,6 +23,7 @@ export default function QuickAccessSection({
starredCount,
archivedCount,
trashedCount,
hiddenCount,
onViewChange,
onFolderSelect,
}: QuickAccessSectionProps) {
Expand All @@ -31,8 +33,9 @@ export default function QuickAccessSection({
starred: starredCount,
archived: archivedCount,
trash: trashedCount,
hidden: hiddenCount,
}),
[notesCount, starredCount, archivedCount, trashedCount]
[notesCount, starredCount, archivedCount, trashedCount, hiddenCount]
);

const handleViewSelect = (view: ViewMode) => {
Expand Down
3 changes: 3 additions & 0 deletions src/components/folders/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ interface FolderPanelProps {
starredCount: number;
archivedCount: number;
trashedCount: number;
hiddenCount: number;
expandedFolders: Set<string>;
onCreateNote: (templateContent?: { title: string; content: string }) => void;
onCreateFolder: (name: string, color: string, parentId?: string) => void;
Expand Down Expand Up @@ -62,6 +63,7 @@ export default function FolderPanel({
starredCount,
archivedCount,
trashedCount,
hiddenCount,
expandedFolders,
onCreateFolder,
onUpdateFolder,
Expand Down Expand Up @@ -235,6 +237,7 @@ export default function FolderPanel({
starredCount={starredCount}
archivedCount={archivedCount}
trashedCount={trashedCount}
hiddenCount={hiddenCount}
onViewChange={onViewChange}
onFolderSelect={onFolderSelect}
/>
Expand Down
7 changes: 6 additions & 1 deletion src/components/layout/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default function MainLayout() {
starredCount,
archivedCount,
trashedCount,
hiddenCount,
expandedFolders,
updateFolder,
createNote,
Expand All @@ -36,6 +37,8 @@ export default function MainLayout() {
deleteNote,
toggleStar,
archiveNote,
hideNote,
unhideNote,
toggleFolderExpansion,
reorderFolders,
setSelectedNote,
Expand Down Expand Up @@ -98,7 +101,7 @@ export default function MainLayout() {
}, [handleUnlockSuccess, reinitialize]);

const folderPanelProps: FolderPanelProps = {
currentView, folders, selectedFolder, searchQuery, notesCount, starredCount, archivedCount, trashedCount, expandedFolders,
currentView, folders, selectedFolder, searchQuery, notesCount, starredCount, archivedCount, trashedCount, hiddenCount, expandedFolders,
onUpdateFolder: (id, name, color) => updateFolder(id, { name, color }),
onUpdateFolderParent: (id, parentId) => updateFolder(id, { parentId }),
onCreateNote: handleCreateNote,
Expand Down Expand Up @@ -128,6 +131,8 @@ export default function MainLayout() {
onDeleteNote: deleteNote,
onArchiveNote: archiveNote,
onToggleStar: toggleStar,
onHideNote: hideNote,
onUnhideNote: unhideNote,
userId,
};

Expand Down
12 changes: 7 additions & 5 deletions src/components/notes/NotesPanel/NoteCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ function NoteCard({
}
};

const formattedDate = useMemo(() => {
return note?.createdAt ? formatDateTime(note.createdAt) : 'No date';
}, [note?.createdAt]);

const previewText = useMemo(() => {
if (note?.hidden) return '[HIDDEN]';

if (!note?.content) return 'No additional text';

const temp = document.createElement('div');
Expand All @@ -50,11 +56,7 @@ function NoteCard({
}

return text || 'No additional text';
}, [note?.content]);

const formattedDate = useMemo(() => {
return note?.updatedAt ? formatDateTime(note.updatedAt) : 'No date';
}, [note?.updatedAt]);
}, [note?.content, note?.hidden]);

const folder = useMemo(() => {
// First check if note has embedded folder data
Expand Down
Loading