• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SNApp-notes / web / 20411192302

21 Dec 2025 02:24PM UTC coverage: 86.434% (+0.3%) from 86.171%
20411192302

push

github

jcubic
fix unit tests

715 of 870 branches covered (82.18%)

Branch coverage included in aggregate %.

1413 of 1592 relevant lines covered (88.76%)

2063.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.73
/src/components/notes/MiddlePanel.tsx
1
/**
2
 * Middle panel component for note editing and content display.
3
 *
4
 * @remarks
5
 * Dependencies: Chakra UI v3, React, Editor component
6
 *
7
 * **Features:**
8
 * - Note title display
9
 * - Real-time save status indicator
10
 * - CodeMirror-based markdown editor
11
 * - Line selection support
12
 * - Empty state for no selected note
13
 * - Editor reference callback for parent control
14
 *
15
 * **Save Status:**
16
 * - idle: No indicator shown
17
 * - saving: Blue "Saving..." text
18
 * - saved: Green "Saved" text
19
 * - error: Red "Save failed" text
20
 *
21
 * **Performance:**
22
 * - Memoized component and computed values
23
 * - useCallback for event handlers
24
 *
25
 * @example
26
 * ```tsx
27
 * <MiddlePanel
28
 *   note={currentNote}
29
 *   content={noteContent}
30
 *   saveStatus="saved"
31
 *   selectedLine={42}
32
 *   onContentChange={handleChange}
33
 *   onEditorReady={(ref) => editorRef.current = ref}
34
 * />
35
 * ```
36
 *
37
 * @public
38
 */
39
'use client';
40

41
import { Box, Text, Flex } from '@chakra-ui/react';
42
import { memo, useMemo, useCallback } from 'react';
43
import type { NoteTreeNode } from '@/types/tree';
44
import type { SaveStatus } from '@/types/notes';
45
import type { EditorRef } from '@/types/editor';
46
import Editor from '@/components/Editor';
47

48
/**
49
 * Props for the MiddlePanel component.
50
 *
51
 * @public
52
 */
53
interface MiddlePanelProps {
54
  /** Currently selected note or null if none selected */
55
  note: NoteTreeNode | null;
56
  /** Current content of the note */
57
  content: string;
58
  /** Current save status of the note */
59
  saveStatus: SaveStatus;
60
  /** Optional line number to highlight in editor */
61
  selectedLine?: number;
62
  /** Optional initial cursor position as character offset from start */
63
  cursorPosition?: number;
64
  /** Callback invoked when content changes */
65
  onContentChange: (content: string) => void;
66
  /** Optional callback invoked when editor is ready */
67
  onEditorReady?: (editorRef: EditorRef) => void;
68
  /** Optional callback invoked when cursor position changes */
69
  onCursorChange?: () => void;
70
  /** Optional callback invoked when scroll position changes */
71
  onScrollChange?: () => void;
72
}
73

74
/**
75
 * Renders the middle panel with note editor and status bar.
76
 *
77
 * @param props - Component props
78
 * @param props.note - Currently selected note
79
 * @param props.content - Note content string
80
 * @param props.saveStatus - Current save status
81
 * @param props.selectedLine - Line number to highlight
82
 * @param props.onContentChange - Handler for content changes
83
 * @param props.onEditorReady - Handler for editor initialization
84
 * @returns Memoized middle panel component
85
 *
86
 * @remarks
87
 * Displays empty state when no note is selected.
88
 * Save status bar shows note name and color-coded status.
89
 *
90
 * @public
91
 */
92
const MiddlePanel = memo(function MiddlePanel({
105✔
93
  note,
94
  content,
95
  saveStatus,
96
  selectedLine,
97
  cursorPosition,
98
  onContentChange,
99
  onEditorReady,
100
  onCursorChange,
101
  onScrollChange
102
}: MiddlePanelProps) {
103
  const saveStatusText = useMemo(() => {
2,748✔
104
    switch (saveStatus) {
324!
105
      case 'saving':
106
        return 'Saving...';
12✔
107
      case 'saved':
108
        return 'Saved';
12✔
109
      case 'error':
110
        return 'Save failed';
×
111
      default:
112
        return '';
300✔
113
    }
114
  }, [saveStatus]);
115

116
  const saveStatusColor = useMemo(() => {
2,748✔
117
    switch (saveStatus) {
324!
118
      case 'saving':
119
        return 'blue.500';
12✔
120
      case 'saved':
121
        return 'green.500';
12✔
122
      case 'error':
123
        return 'red.500';
×
124
      default:
125
        return 'gray.500';
300✔
126
    }
127
  }, [saveStatus]);
128

129
  const handleContentChange = useCallback(
2,748✔
130
    (value: string | undefined) => {
131
      onContentChange(value || '');
1,016!
132
    },
133
    [onContentChange]
134
  );
135

136
  return (
×
137
    <Box as="main" h="100%" display="flex" flexDirection="column">
138
      {/* Save status bar */}
139
      <Flex
140
        justify="space-between"
141
        align="center"
142
        p={3}
143
        borderBottom="1px solid"
144
        borderColor="border"
145
      >
146
        <Text fontSize="lg" fontWeight="semibold">
147
          {note?.name || 'Select a note'}
3,026✔
148
        </Text>
149
        {saveStatus !== 'idle' && (
2,748!
150
          <Text fontSize="sm" color={saveStatusColor}>
151
            {saveStatusText}
152
          </Text>
153
        )}
154
      </Flex>
155

156
      {/* Editor area */}
157
      <Box flex={1} overflow="hidden" position="relative" minH={0}>
158
        {note ? (
×
159
          <Editor
2,470✔
160
            key={note.id}
161
            value={content}
162
            cursorPosition={cursorPosition}
163
            onChange={handleContentChange}
164
            selectedLine={selectedLine}
165
            onEditorReady={onEditorReady}
166
            onCursorChange={onCursorChange}
167
            onScrollChange={onScrollChange}
168
            placeholder="Start writing your note..."
169
          />
170
        ) : (
171
          <Flex h="100%" align="center" justify="center">
172
            <Text color="fg.muted">Select or create a note to start editing</Text>
173
          </Flex>
174
        )}
175
      </Box>
176
    </Box>
177
  );
178
});
179

180
export default MiddlePanel;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc