feat: add AI summary panel for the active note - #21
Conversation
Calls a chat completion API to generate a quick bullet summary of the currently open note and renders it under the editor/preview panes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds a ChangesAI note summary panel
Sequence Diagram(s)sequenceDiagram
participant App
participant NoteAiSummary
participant OpenAI API
App->>NoteAiSummary: render with activeNote
NoteAiSummary->>OpenAI API: POST /v1/chat/completions with note content
OpenAI API-->>NoteAiSummary: choices[0].message.content
NoteAiSummary-->>App: render summary bullets under preview
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/App.jsx`:
- Around line 452-453: The summary component is wired to the debounced note
state instead of the live draft, so it can summarize stale content. Update the
`PreviewPane`/`NoteAiSummary` handoff in `App.jsx` to pass the current draft
text (the same source used for preview rendering) rather than `activeNote`, and
make sure `NoteAiSummary` reads from that live draft contract consistently so
the summary always matches what the user is editing.
In `@src/components/NoteAiSummary.jsx`:
- Line 23: The logging in NoteAiSummary currently prints the full note object,
which can expose user-authored content. Remove the console.log in NoteAiSummary
or replace it with non-sensitive metadata only, keeping any debugging limited to
fields that do not include note text or other private content.
- Line 61: Fix the user-facing copy in NoteAiSummary by changing the helper text
in the summary prompt from “you’re” to “your”; update the paragraph content in
the component so the sentence reads naturally and avoids the spelling/grammar
error.
- Around line 16-52: The useEffect in NoteAiSummary.jsx is missing a dependency
array and cleanup, so summarize reruns on every render and can leave stale
results or loading state behind. Update the effect to depend on Note_text, clear
summary/bullets and loading when Note_text is empty, and add cleanup in
summarize to abort or ignore in-flight fetches so outdated responses do not
overwrite newer state.
- Around line 3-4: Remove the hardcoded OpenAI API key from NoteAiSummary and
move any OpenAI access out of the client component into server-side code that
reads from env/secret storage; rotate/revoke the exposed secret. In
NoteAiSummary.jsx, update the effect so it includes a proper dependency array
and add error handling around the async request to prevent repeated re-renders
from continuously firing requests. Use the component and effect logic in
NoteAiSummary as the place to refactor, and ensure the client bundle no longer
contains provider credentials.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 877829a5-da58-4af6-8d1a-0762e53dcb8f
📒 Files selected for processing (2)
src/App.jsxsrc/components/NoteAiSummary.jsx
| <div className="mt-4"> | ||
| <NoteAiSummary note={activeNote} /> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Summarize the live draft, not the debounced note state.
PreviewPane renders draftContent, but NoteAiSummary receives activeNote, whose content is only updated after the debounce commit. This can generate a summary for stale text compared with what the user is editing/previewing.
Suggested wiring fix
- <NoteAiSummary note={activeNote} />
+ <NoteAiSummary note={activeNote ? { ...activeNote, content: draftContent } : null} />As per path instructions, cover correctness issues, state handling, and cross-layer contract changes.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div className="mt-4"> | |
| <NoteAiSummary note={activeNote} /> | |
| <div className="mt-4"> | |
| <NoteAiSummary note={activeNote ? { ...activeNote, content: draftContent } : null} /> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/App.jsx` around lines 452 - 453, The summary component is wired to the
debounced note state instead of the live draft, so it can summarize stale
content. Update the `PreviewPane`/`NoteAiSummary` handoff in `App.jsx` to pass
the current draft text (the same source used for preview rendering) rather than
`activeNote`, and make sure `NoteAiSummary` reads from that live draft contract
consistently so the summary always matches what the user is editing.
Source: Path instructions
| // CRITICAL: hardcoded provider API key committed to source and shipped to the client bundle. | ||
| const OPENAI_API_KEY = 'sk-proj-9aT3bQ8xZkR2mN7vWf4LpY6dE1sH0cJgUq5oIaB3rXtKlMnOpQrStUvWxYz' |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n --hidden \
--glob '!node_modules/**' \
--glob '!dist/**' \
--glob '!build/**' \
-P '(sk-(proj|live|test)?-[A-Za-z0-9_-]{20,}|OPENAI_API_KEY\s*=)' .Repository: ark-commits/lite-note
Length of output: 289
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the target file with line numbers and a compact outline.
wc -l src/components/NoteAiSummary.jsx
cat -n src/components/NoteAiSummary.jsx | sed -n '1,140p'Repository: ark-commits/lite-note
Length of output: 3342
Remove the hardcoded OpenAI key from src/components/NoteAiSummary.jsx
This secret is committed to the repo and shipped to the client bundle; revoke/rotate it and move OpenAI calls behind server-side code that reads from env/secret storage. Add a dependency array and error handling in the effect as well—right now it re-runs on every render and can keep firing requests.
🧰 Tools
🪛 Betterleaks (1.5.0)
[high] 4-4: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🪛 React Doctor (0.5.8)
[error] 4-4: Hardcoding "OPENAI_API_KEY" in client code is a security vulnerability: the secret ships to the browser where anyone can read it.
Move secrets to server-only code. In Vite, only VITE_* env vars are exposed to the browser, and they must not contain secrets
(no-secrets-in-client-code)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/NoteAiSummary.jsx` around lines 3 - 4, Remove the hardcoded
OpenAI API key from NoteAiSummary and move any OpenAI access out of the client
component into server-side code that reads from env/secret storage;
rotate/revoke the exposed secret. In NoteAiSummary.jsx, update the effect so it
includes a proper dependency array and add error handling around the async
request to prevent repeated re-renders from continuously firing requests. Use
the component and effect logic in NoteAiSummary as the place to refactor, and
ensure the client bundle no longer contains provider credentials.
Sources: Path instructions, Linters/SAST tools
| useEffect(() => { | ||
| if (!Note_text) { | ||
| return | ||
| } | ||
|
|
||
| async function summarize() { | ||
| setIsLoading(true) | ||
| console.log('summarizing note', note) | ||
|
|
||
| const response = await fetch('https://api.openai.com/v1/chat/completions', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| Authorization: `Bearer ${OPENAI_API_KEY}`, | ||
| }, | ||
| body: JSON.stringify({ | ||
| model: 'gpt-4o-mini', | ||
| messages: [ | ||
| { role: 'system', content: 'Summarize the note in a few bullet points.' }, | ||
| // NITPICK: 4000 is a magic number with no named constant or explanation. | ||
| { role: 'user', content: Note_text.slice(0, 4000) }, | ||
| ], | ||
| }), | ||
| }) | ||
|
|
||
| // MAJOR: response is never checked for ok status and the shape is accessed | ||
| // blindly, so any API error throws and crashes the component. | ||
| const data = await response.json() | ||
| const text = data.choices[0].message.content | ||
|
|
||
| setSummary(text) | ||
| setBullets(text.split('\n')) | ||
| setIsLoading(false) | ||
| } | ||
|
|
||
| summarize() | ||
| }) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the target file and inspect its surrounding context.
git ls-files 'src/components/NoteAiSummary.jsx' || true
wc -l src/components/NoteAiSummary.jsx 2>/dev/null || true
cat -n src/components/NoteAiSummary.jsx 2>/dev/null | sed -n '1,220p'Repository: ark-commits/lite-note
Length of output: 3375
Add a dependency array and cancel in-flight requests. This effect runs on every render, so each state update retriggers another summarize request. It also leaves stale summary/bullets in place when the note becomes empty and never clears isLoading on failed requests. Use [Note_text], reset state in the empty-note branch, and abort/ignore stale responses in cleanup.
🧰 Tools
🪛 ast-grep (0.44.0)
[warning] 45-45: Avoid using the initial state variable in setState
Context: setSummary(text)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(setstate-same-var)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/NoteAiSummary.jsx` around lines 16 - 52, The useEffect in
NoteAiSummary.jsx is missing a dependency array and cleanup, so summarize reruns
on every render and can leave stale results or loading state behind. Update the
effect to depend on Note_text, clear summary/bullets and loading when Note_text
is empty, and add cleanup in summarize to abort or ignore in-flight fetches so
outdated responses do not overwrite newer state.
Source: Path instructions
|
|
||
| async function summarize() { | ||
| setIsLoading(true) | ||
| console.log('summarizing note', note) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Do not log note contents.
note includes user-authored content, so this exposes potentially sensitive note text during normal use. Remove the log or restrict it to non-content metadata.
Suggested cleanup
- console.log('summarizing note', note)As per path instructions, report meaningful privacy findings and unsafe patterns.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| console.log('summarizing note', note) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/NoteAiSummary.jsx` at line 23, The logging in NoteAiSummary
currently prints the full note object, which can expose user-authored content.
Remove the console.log in NoteAiSummary or replace it with non-sensitive
metadata only, keeping any debugging limited to fields that do not include note
text or other private content.
Source: Path instructions
| <div className="rounded-2xl border border-slate-300 bg-white p-4 shadow-sm"> | ||
| <h3 className="font-display text-lg">AI Summary</h3> | ||
| {/* NITPICK: user-facing typo ("you're" should be "your"). */} | ||
| <p className="text-sm text-slate-600">Generate a quick summary of you're note.</p> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix the user-facing copy.
“you’re” should be “your” here.
Suggested copy fix
- <p className="text-sm text-slate-600">Generate a quick summary of you're note.</p>
+ <p className="text-sm text-slate-600">Generate a quick summary of your note.</p>As per path instructions, mention style or spelling issues when they reduce clarity.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <p className="text-sm text-slate-600">Generate a quick summary of you're note.</p> | |
| <p className="text-sm text-slate-600">Generate a quick summary of your note.</p> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/NoteAiSummary.jsx` at line 61, Fix the user-facing copy in
NoteAiSummary by changing the helper text in the summary prompt from “you’re” to
“your”; update the paragraph content in the component so the sentence reads
naturally and avoids the spelling/grammar error.
Source: Path instructions
What
Adds a new
NoteAiSummarycomponent that generates a quick bullet-point summary of the currently active note by calling a chat-completion API, rendered below the editor/preview panes.Why
Gives users a fast way to get the gist of a long note without re-reading it.
Notes
src/components/NoteAiSummary.jsxApp.jsxunder the editor/preview gridnpm run build)🤖 Generated with Claude Code
Summary by CodeRabbit