77 FolderInput ,
88 Paperclip ,
99 Printer ,
10+ EyeOff ,
11+ Eye ,
1012} from 'lucide-react' ;
1113import { Button } from '@/components/ui/button' ;
1214import {
@@ -16,8 +18,9 @@ import {
1618 DropdownMenuSeparator ,
1719 DropdownMenuTrigger ,
1820} from '@/components/ui/dropdown-menu' ;
19- import { useEditor , EditorContent } from '@tiptap/react' ;
21+ import { useEditor , EditorContent , Editor } from '@tiptap/react' ;
2022import ReactDOM from 'react-dom/client' ;
23+ import type { Root } from 'react-dom/client' ;
2124import tippy from 'tippy.js' ;
2225import { createEditorExtensions } from './config/editor-config' ;
2326import { SlashCommands } from './extensions/SlashCommandsExtension' ;
@@ -33,13 +36,38 @@ import { useEditorEffects } from '@/components/editor/hooks/useEditorEffects';
3336import { fileService } from '@/services/fileService' ;
3437import type { Note , Folder as FolderType , FileAttachment } from '@/types/note' ;
3538
39+ // Type definitions for TipTap Suggestion
40+ // Import CommandItem type from SlashCommands
41+ import type { CommandItem } from './extensions/SlashCommands' ;
42+
43+ interface SuggestionProps {
44+ editor : Editor ;
45+ range : { from : number ; to : number } ;
46+ query : string ;
47+ text : string ;
48+ command : ( item : CommandItem ) => void ;
49+ clientRect ?: ( ) => DOMRect | null ;
50+ decorationNode ?: Element | null ;
51+ virtualNode ?: Element | null ;
52+ }
53+
54+ interface SuggestionKeyDownProps {
55+ event : KeyboardEvent ;
56+ }
57+
58+ interface SlashCommandsHandle {
59+ onKeyDown : ( props : SuggestionKeyDownProps ) => boolean ;
60+ }
61+
3662interface NoteEditorProps {
3763 note : Note | null ;
3864 folders ?: FolderType [ ] ;
3965 onUpdateNote : ( noteId : string , updates : Partial < Note > ) => void ;
4066 onDeleteNote : ( noteId : string ) => void ;
4167 onArchiveNote : ( noteId : string ) => void ;
4268 onToggleStar : ( noteId : string ) => void ;
69+ onHideNote : ( noteId : string ) => void ;
70+ onUnhideNote : ( noteId : string ) => void ;
4371 userId ?: string ;
4472}
4573
@@ -50,6 +78,8 @@ export default function Index({
5078 onDeleteNote,
5179 onArchiveNote,
5280 onToggleStar,
81+ onHideNote,
82+ onUnhideNote,
5383 userId = 'current-user' ,
5484} : NoteEditorProps ) {
5585 const [ isMoveModalOpen , setIsMoveModalOpen ] = useState ( false ) ;
@@ -80,6 +110,7 @@ export default function Index({
80110
81111 const editor = useEditor (
82112 {
113+ editable : ! note ?. hidden ,
83114 extensions : [
84115 ...createEditorExtensions ( ) ,
85116 SlashCommands . configure ( {
@@ -88,23 +119,20 @@ export default function Index({
88119 return [ ] ;
89120 } ,
90121 render : ( ) => {
91- // eslint-disable-next-line @typescript-eslint/no-explicit-any
92- let component : any ;
93- // eslint-disable-next-line @typescript-eslint/no-explicit-any
94- let popup : any ;
95- // eslint-disable-next-line @typescript-eslint/no-explicit-any
96- let root : any ;
122+ let component : SlashCommandsHandle | null = null ;
123+ let popup : ReturnType < typeof tippy > [ 0 ] | null = null ;
124+ let root : Root | null = null ;
97125
98126 return {
99- onStart : ( props : any ) => { // eslint-disable-line @typescript-eslint/no-explicit-any
127+ onStart : ( props : SuggestionProps ) => {
100128 if ( ! props . clientRect ) {
101129 return ;
102130 }
103131
104132 const container = document . createElement ( 'div' ) ;
105133
106134 popup = tippy ( 'body' , {
107- getReferenceClientRect : props . clientRect ,
135+ getReferenceClientRect : props . clientRect as ( ) => DOMRect ,
108136 appendTo : ( ) => document . body ,
109137 content : container ,
110138 showOnCreate : true ,
@@ -115,35 +143,23 @@ export default function Index({
115143
116144 root = ReactDOM . createRoot ( container ) ;
117145
118- const commandsList = document . createElement ( 'div' ) ;
119- component = ReactDOM . createRoot ( commandsList ) ;
120-
121- component . render (
122- < SlashCommandsList
123- ref = { ( ref : any ) => { // eslint-disable-line @typescript-eslint/no-explicit-any
124- component = ref ;
125- } }
126- command = { props . command }
127- />
128- ) ;
129-
130146 root . render (
131147 < SlashCommandsList
132- ref = { ( ref : any ) => { // eslint-disable-line @typescript-eslint/no-explicit-any
148+ ref = { ( ref : SlashCommandsHandle | null ) => {
133149 component = ref ;
134150 } }
135151 command = { props . command }
136152 />
137153 ) ;
138154 } ,
139155
140- onUpdate : ( props : any ) => { // eslint-disable-line @typescript-eslint/no-explicit-any
156+ onUpdate : ( props : SuggestionProps ) => {
141157 popup ?. setProps ( {
142- getReferenceClientRect : props . clientRect ,
158+ getReferenceClientRect : props . clientRect as ( ) => DOMRect ,
143159 } ) ;
144160 } ,
145161
146- onKeyDown : ( props : any ) => { // eslint-disable-line @typescript-eslint/no-explicit-any
162+ onKeyDown : ( props : SuggestionKeyDownProps ) => {
147163 if ( props . event . key === 'Escape' ) {
148164 popup ?. hide ( ) ;
149165 return true ;
@@ -161,7 +177,7 @@ export default function Index({
161177 } ,
162178 } ) ,
163179 ] ,
164- content : note ?. content || '' ,
180+ content : note ?. hidden ? '[HIDDEN]' : ( note ?. content || '' ) ,
165181 editorProps : {
166182 attributes : {
167183 class :
@@ -246,7 +262,7 @@ export default function Index({
246262 } ,
247263 } ,
248264 onUpdate : ( { editor } ) => {
249- if ( ! note ) return ;
265+ if ( ! note || note . hidden ) return ; // Prevent auto-save for hidden notes
250266
251267 // Update word and character counts
252268 const text = editor . state . doc . textContent ;
@@ -275,7 +291,7 @@ export default function Index({
275291 }
276292 } ,
277293 } ,
278- [ note ?. id , updateCounts ]
294+ [ note ?. id , note ?. hidden , updateCounts ]
279295 ) ;
280296
281297 // Use custom hook for editor effects
@@ -292,6 +308,22 @@ export default function Index({
292308
293309
294310
311+ // Update editor when note hidden state changes
312+ useEffect ( ( ) => {
313+ if ( editor && note ) {
314+ const currentContent = editor . getHTML ( ) ;
315+
316+ // Only update if content actually changed
317+ if ( note . hidden && currentContent !== '[HIDDEN]' ) {
318+ editor . commands . setContent ( '[HIDDEN]' ) ;
319+ editor . setEditable ( false ) ;
320+ } else if ( ! note . hidden && currentContent === '[HIDDEN]' ) {
321+ editor . commands . setContent ( note . content || '' ) ;
322+ editor . setEditable ( true ) ;
323+ }
324+ }
325+ } , [ editor , note ] ) ;
326+
295327 // Cleanup timeout on unmount
296328 useEffect ( ( ) => {
297329 return ( ) => {
@@ -476,6 +508,7 @@ export default function Index({
476508 onChange = { handleTitleChange }
477509 className = "text-foreground placeholder-muted-foreground min-w-0 flex-1 border-none bg-transparent text-2xl font-bold outline-none"
478510 placeholder = "Untitled Note"
511+ disabled = { note . hidden }
479512 />
480513 </ div >
481514
@@ -508,12 +541,29 @@ export default function Index({
508541 className = {
509542 note . starred ? 'text-yellow-500' : 'text-muted-foreground'
510543 }
544+ title = { note . starred ? 'Remove from starred' : 'Add to starred' }
511545 >
512546 < Star
513547 className = { `h-4 w-4 ${ note . starred ? 'fill-current' : '' } ` }
514548 />
515549 </ Button >
516550
551+ < Button
552+ variant = "ghost"
553+ size = "sm"
554+ onClick = { ( ) => note . hidden ? onUnhideNote ( note . id ) : onHideNote ( note . id ) }
555+ className = {
556+ note . hidden ? 'text-primary' : 'text-muted-foreground'
557+ }
558+ title = { note . hidden ? 'Unhide note' : 'Hide note' }
559+ >
560+ { note . hidden ? (
561+ < EyeOff className = "h-4 w-4" />
562+ ) : (
563+ < Eye className = "h-4 w-4" />
564+ ) }
565+ </ Button >
566+
517567 < DropdownMenu >
518568 < DropdownMenuTrigger asChild >
519569 < Button variant = "ghost" size = "sm" >
@@ -523,7 +573,7 @@ export default function Index({
523573 < DropdownMenuContent align = "end" >
524574 < DropdownMenuItem onClick = { ( ) => onToggleStar ( note . id ) } >
525575 < Star className = "mr-2 h-4 w-4" />
526- { note . starred ? 'Remove from Starred ' : 'Star' }
576+ { note . starred ? 'Unstar ' : 'Star' }
527577 </ DropdownMenuItem >
528578 < DropdownMenuItem onClick = { ( ) => setIsMoveModalOpen ( true ) } >
529579 < FolderInput className = "mr-2 h-4 w-4" />
@@ -585,9 +635,11 @@ export default function Index({
585635 </ div >
586636 </ div >
587637
588- < div className = "flex-shrink-0" >
589- < Toolbar editor = { editor } />
590- </ div >
638+ { ! note . hidden && (
639+ < div className = "flex-shrink-0" >
640+ < Toolbar editor = { editor } />
641+ </ div >
642+ ) }
591643
592644 { showAttachments && (
593645 < >
0 commit comments