feat: AI tagging, extension polish, nav/auth UX + security hardening#22
Conversation
- Add Gemini AI auto-tagging on bookmark save (lib/gemini.ts) - Extension: add brand icon (PNG), manage bookmarks link, Alt+Shift+S shortcut, fix host_permissions for Vercel - Landing page nav: differentiate Sign in (?mode=login) vs Get started (?mode=signup) - Auth page: read ?mode param on load, add explicit back-to-home link - Fix all <img> → <Image /> ESLint warnings (page.tsx, Header, BookmarkCard) - Add next/image remotePatterns wildcard for external thumbnails - Run npm audit fix — 0 vulnerabilities remaining - Update README with full rewrite, extension install guide, feature status - Update CLAUDE.md and GUIDE.md: P2.4 AI tagging marked done Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note
|
| Cohort / File(s) | Summary |
|---|---|
Documentation CLAUDE.md, README.md |
Updated tech versions (Tailwind v4, Prisma 7), declared deployment (Vercel live), added Upstash Redis and Vercel Analytics, rewrote roadmap/milestone/state sections, expanded install/run and extension manual-install instructions, updated contributing and acknowledgements. |
Next.js image config & components client/next.config.ts, client/src/components/BookmarkCard.tsx, client/src/components/Header.tsx, client/src/app/page.tsx |
Added images.remotePatterns for http/https in Next config; replaced native <img> with next/image in header and bookmark thumbnails; updated auth navigation links to include mode query. |
Bookmarks API & AI tagging client/src/app/api/bookmarks/route.ts, client/src/lib/gemini.ts |
Added generateTags() using Google GenAI (gemini-2.5-flash); POST /api/bookmarks calls generateTags and inserts tags via prisma.tag.createMany (skipDuplicates) and returns { ...bookmark, aiTagsFailed }; GET now includes tags. |
Auth UI client/src/app/auth/page.tsx |
Auth page now reads mode from query params via useSearchParams, splits into AuthForm + AuthPage, wrapped in Suspense, and adds a back-to-home link. |
Extension UI & behavior extension/manifest.json, extension/popup.html, extension/popup.css, extension/popup.js |
Added icons and save-bookmark keyboard command (Alt+Shift+S), added production host permission, added password-visibility toggle UI + handler, added management link, improved 401 detection and save feedback (shows aiTagsFailed message). |
Extension background / commands extension/serviceworker.js |
Replaced placeholder worker with MV3 service worker: added getSession() helper and chrome.commands.onCommand handler for save-bookmark that reads active tab, validates URL/session, posts to /api/bookmarks, handles 401 by opening popup, and sets per-tab badges on success/error. |
Sequence Diagram
sequenceDiagram
participant Ext as Extension (User)
participant API as Recall API
participant Gemini as Google GenAI
participant DB as Database (Prisma)
Ext->>API: POST /api/bookmarks { url, Authorization }
activate API
API->>API: Scrape metadata (title, description)
API->>Gemini: generateTags(title, description)
activate Gemini
Gemini-->>API: JSON array of tag strings
deactivate Gemini
API->>DB: create bookmark record
API->>DB: createMany(tags) with bookmarkId (skipDuplicates)
DB-->>API: persisted bookmark + tags
API-->>Ext: { bookmark, aiTagsFailed }
deactivate API
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
- M4: bookmark list UI renders saved items #2 — Modifies the bookmarks API handler and bookmark storage/validation; overlaps with the POST/GET changes here.
- M6 manual tags #5 — Adds manual tag CRUD and tag-related schema/UI; closely related to AI tag insertion and tag-shaped responses.
- M8: Chrome extension — login, save, list, delete bookmarks #19 — Overlapping extension changes (manifest, popup, service worker) that intersect with extension/* updates in this PR.
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | The title accurately captures the main changes: AI tagging feature, extension improvements, navigation/auth UX updates, and security hardening. |
| Description check | ✅ Passed | The PR description provides comprehensive coverage of all changes, follows the template structure with type selection and completed checklist items, though the summary format is slightly informal. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
📝 Generate docstrings
- Create stacked PR
- Commit on current branch
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Commit unit tests in branch
p2-ai-tagging
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
client/src/app/page.tsx (2)
410-418:⚠️ Potential issue | 🟡 MinorSame inconsistency: final CTA also missing
modeparameter.Similar to the hero CTA, this "Start saving for free" link should include
?mode=signupfor consistency with the nav behavior.🔧 Proposed fix
- <Link href="/auth" style={{ + <Link href="/auth?mode=signup" style={{🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/app/page.tsx` around lines 410 - 418, The final CTA Link in page.tsx (the Link element rendering "Start saving for free →") is missing the mode query param; update its href to include "?mode=signup" (e.g., change href from "/auth" to "/auth?mode=signup") so it matches the nav/hero CTA behavior and consistently triggers signup mode in the Auth flow.
118-127:⚠️ Potential issue | 🟡 MinorInconsistent auth link: missing
modeparameter.The hero CTA "Start saving for free" links to
/authwithout amodeparameter, while the nav links at lines 33-36 correctly use/auth?mode=signupand/auth?mode=login. This inconsistency means this CTA will default to login mode instead of signup.🔧 Proposed fix for consistency
- <Link href="/auth" style={{ + <Link href="/auth?mode=signup" style={{🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/app/page.tsx` around lines 118 - 127, The hero CTA Link currently points to "/auth" (the Link element rendering "Start saving for free") which is inconsistent with nav links using "/auth?mode=signup" and causes it to default to login; update that Link's href to include the signup query parameter (e.g., "/auth?mode=signup") so the CTA opens the signup flow, leaving all other inline styles and attributes unchanged.
🧹 Nitpick comments (4)
client/next.config.ts (1)
4-15: Wildcard hostname configuration is intentional but worth documenting.The
hostname: "**"pattern allows images from any external domain, which is necessary for arbitrary OG thumbnails from bookmarked URLs. SinceBookmarkCardusesunoptimized, the images bypass the optimization proxy anyway, reducing SSRF risk.Consider adding a comment explaining why this permissive configuration is required:
📝 Add explanatory comment
const nextConfig: NextConfig = { + // Allow images from any hostname because bookmark thumbnails + // come from arbitrary external websites' OG metadata. + // BookmarkCard uses `unoptimized` to bypass the optimization proxy. images: { remotePatterns: [🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/next.config.ts` around lines 4 - 15, Add a short explanatory comment above the images.remotePatterns block in next.config.ts that documents why hostname: "**" is intentionally allowed (to permit arbitrary external OG thumbnails for bookmarked URLs) and note that BookmarkCard uses unoptimized images so they bypass the Next image optimization proxy, reducing SSRF risk; mention any remaining precautions or rationale so future reviewers understand this permissive configuration and its trade-offs.client/src/app/auth/page.tsx (1)
248-254: Consider adding a fallback UI to the Suspense boundary.The
Suspensewrapper is correctly required foruseSearchParams()in Next.js App Router, but an empty fallback means users see nothing during the brief hydration period. A minimal loading indicator would improve perceived performance.✨ Optional: Add a loading fallback
export default function AuthPage() { return ( - <Suspense> + <Suspense fallback={<div className="min-h-screen bg-[var(--bg)]" />}> <AuthForm /> </Suspense> ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/app/auth/page.tsx` around lines 248 - 254, AuthPage's Suspense wrapper has no fallback, causing a blank UI during hydration; add a minimal fallback (e.g., a Loading text or small spinner) to the Suspense that wraps AuthForm so users see a loading indicator while useSearchParams() resolves—update the Suspense in the AuthPage component to include a fallback prop (or reference a small Loading/Spinner component) so the UI shows immediately.client/src/components/BookmarkCard.tsx (1)
106-113: Consider adding error handling for failed image loads.External thumbnail URLs from OG metadata may return 404, be CORS-blocked, or fail to load for various reasons. Without an
onErrorhandler, the component will display a broken image state.🛡️ Proposed fix to handle image load failures
+import { useState } from "react"; +// ... existing imports export default function BookmarkCard({ bookmark }: { bookmark: Bookmark }) { const router = useRouter(); const [input, setInput] = useState(""); + const [imgError, setImgError] = useState(false); // ... rest of state // In the render: - {bookmark.thumbnail && ( + {bookmark.thumbnail && !imgError && ( <Image src={bookmark.thumbnail} alt={bookmark.title ?? ""} width={64} height={64} className="w-16 h-16 flex-shrink-0 rounded-xl object-cover bg-[var(--surface-2)]" unoptimized + onError={() => setImgError(true)} /> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/components/BookmarkCard.tsx` around lines 106 - 113, The Image in BookmarkCard using src={bookmark.thumbnail} lacks error handling and can show a broken image when remote thumbnails fail; update the BookmarkCard component to add an onError handler (or local state-managed fallbackSrc) that swaps the image to a safe local placeholder (e.g., a bundled SVG/PNG) or hides the image when loading fails, ensure the handler guards against infinite replacement loops, and reference the Image element using bookmark.thumbnail and alt={bookmark.title} so the fallback is applied only for this card.extension/serviceworker.js (1)
41-43: Avoid fully silent network failures in the command path.Lines [41]-[43] swallow all fetch errors. Add lightweight telemetry (e.g.,
console.warnin dev or a debug flag) so outages/auth issues are diagnosable.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extension/serviceworker.js` around lines 41 - 43, The current catch block after the network fetch in serviceworker.js silently swallows all errors; update the catch in the try/catch around the fetch so it emits lightweight telemetry (e.g., call console.warn or a debug logger with the caught Error) but only when in dev or when a debug flag is enabled (check an existing debug flag or use self.__DEV__ / a configurable DEBUG constant); include the error.message and a short context string (e.g., "serviceworker fetch failed for <operation>") so outages/auth issues can be diagnosed without leaking in production.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@client/src/lib/gemini.ts`:
- Line 3: The module currently uses a non-null assertion when creating the
GoogleGenAI client (const ai = new GoogleGenAI({ apiKey:
process.env.GEMINI_API_KEY! })), which causes opaque import-time failures if
GEMINI_API_KEY is missing; change this to either (a) lazy-initialize the client
via a getClient() helper that constructs GoogleGenAI on first use (and call
getClient() from generateTags), or (b) perform early validation at module
initialization: check process.env.GEMINI_API_KEY and throw a clear, descriptive
error if missing before constructing GoogleGenAI; remove the non-null assertion
and ensure the chosen approach references the GoogleGenAI instantiation and
generateTags usage.
In `@extension/serviceworker.js`:
- Around line 21-23: The current check only verifies tab.url exists and can pass
non-web schemes to your backend; update the logic after chrome.tabs.query (the
const [tab] = await chrome.tabs.query(...) block) to validate the URL protocol
is http or https (e.g., inspect tab.url's protocol via URL or
startsWith('http://')/startsWith('https://')) and return early if it is not, and
apply the same validation at the other occurrence around the POST call (the
similar check at line ~31) so only http/https pages are forwarded to the
backend.
- Line 17: Calls to chrome.action.openPopup() and the chrome.action badge APIs
are not handling returned Promises and tab.id is used without explicit
validation; update the service worker to await or attach .catch handlers to
chrome.action.openPopup() and all chrome.action.* calls (e.g.,
chrome.action.setBadgeText, chrome.action.setBadgeBackgroundColor) so rejections
are not unhandled, and before performing badge operations validate that the tab
object has a numeric id (e.g., typeof tab.id === "number") and only call badge
APIs when that check passes; apply these changes around the existing
chrome.action.openPopup() call and the badge-related code blocks that reference
tab.id.
---
Outside diff comments:
In `@client/src/app/page.tsx`:
- Around line 410-418: The final CTA Link in page.tsx (the Link element
rendering "Start saving for free →") is missing the mode query param; update its
href to include "?mode=signup" (e.g., change href from "/auth" to
"/auth?mode=signup") so it matches the nav/hero CTA behavior and consistently
triggers signup mode in the Auth flow.
- Around line 118-127: The hero CTA Link currently points to "/auth" (the Link
element rendering "Start saving for free") which is inconsistent with nav links
using "/auth?mode=signup" and causes it to default to login; update that Link's
href to include the signup query parameter (e.g., "/auth?mode=signup") so the
CTA opens the signup flow, leaving all other inline styles and attributes
unchanged.
---
Nitpick comments:
In `@client/next.config.ts`:
- Around line 4-15: Add a short explanatory comment above the
images.remotePatterns block in next.config.ts that documents why hostname: "**"
is intentionally allowed (to permit arbitrary external OG thumbnails for
bookmarked URLs) and note that BookmarkCard uses unoptimized images so they
bypass the Next image optimization proxy, reducing SSRF risk; mention any
remaining precautions or rationale so future reviewers understand this
permissive configuration and its trade-offs.
In `@client/src/app/auth/page.tsx`:
- Around line 248-254: AuthPage's Suspense wrapper has no fallback, causing a
blank UI during hydration; add a minimal fallback (e.g., a Loading text or small
spinner) to the Suspense that wraps AuthForm so users see a loading indicator
while useSearchParams() resolves—update the Suspense in the AuthPage component
to include a fallback prop (or reference a small Loading/Spinner component) so
the UI shows immediately.
In `@client/src/components/BookmarkCard.tsx`:
- Around line 106-113: The Image in BookmarkCard using src={bookmark.thumbnail}
lacks error handling and can show a broken image when remote thumbnails fail;
update the BookmarkCard component to add an onError handler (or local
state-managed fallbackSrc) that swaps the image to a safe local placeholder
(e.g., a bundled SVG/PNG) or hides the image when loading fails, ensure the
handler guards against infinite replacement loops, and reference the Image
element using bookmark.thumbnail and alt={bookmark.title} so the fallback is
applied only for this card.
In `@extension/serviceworker.js`:
- Around line 41-43: The current catch block after the network fetch in
serviceworker.js silently swallows all errors; update the catch in the try/catch
around the fetch so it emits lightweight telemetry (e.g., call console.warn or a
debug logger with the caught Error) but only when in dev or when a debug flag is
enabled (check an existing debug flag or use self.__DEV__ / a configurable DEBUG
constant); include the error.message and a short context string (e.g.,
"serviceworker fetch failed for <operation>") so outages/auth issues can be
diagnosed without leaking in production.
🪄 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
Run ID: dac0dd77-adf4-46d8-9c57-454acb5110b0
⛔ Files ignored due to path filters (4)
client/package-lock.jsonis excluded by!**/package-lock.jsonextension/icon128.pngis excluded by!**/*.pngextension/icon16.pngis excluded by!**/*.pngextension/icon48.pngis excluded by!**/*.png
📒 Files selected for processing (14)
CLAUDE.mdREADME.mdclient/next.config.tsclient/src/app/api/bookmarks/route.tsclient/src/app/auth/page.tsxclient/src/app/page.tsxclient/src/components/BookmarkCard.tsxclient/src/components/Header.tsxclient/src/lib/gemini.tsextension/manifest.jsonextension/popup.cssextension/popup.htmlextension/popup.jsextension/serviceworker.js
- gemini.ts: replace module-level non-null assertion with lazy getClient()
helper that validates GEMINI_API_KEY and throws a clear error if missing
- serviceworker.js: add .catch(() => {}) to all chrome.action.openPopup()
and badge API calls to prevent unhandled promise rejections
- serviceworker.js: validate tab.id is a number before passing to badge APIs
- serviceworker.js: validate URL protocol is http/https before forwarding
to the backend, rejecting chrome://, file://, etc.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@extension/serviceworker.js`:
- Around line 39-50: The current response handling swallows all non-OK responses
except 401 and the catch block silently ignores network errors; update the block
that checks res (the code using res, tabId, chrome.action.setBadgeText,
chrome.action.setBadgeBackgroundColor, and chrome.action.openPopup) so that any
non-ok status (e.g., 400, 429, 500) visibly surfaces failure: set a distinct
badge (e.g., "!" or "✖") with a red background for tabId, keep the badge for a
few seconds (use the same setTimeout clearing logic), and optionally call
chrome.action.openPopup() or another user-visible notification for serious
errors; also ensure the catch block similarly sets the error badge when tabId is
defined instead of silently returning.
🪄 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
Run ID: 5c192b5d-6184-4fe3-b8a1-35db7e67c39b
📒 Files selected for processing (2)
client/src/lib/gemini.tsextension/serviceworker.js
🚧 Files skipped from review as they are similar to previous changes (1)
- client/src/lib/gemini.ts
Non-OK responses (400, 429, 500) and network errors now show a red ✖ badge for 3 seconds instead of silently doing nothing. 401 still opens the popup. Success keeps the existing gold ✓ badge. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (3)
extension/serviceworker.js (3)
47-59: Extract duplicated error badge logic into a helper.The error badge logic (lines 48-52 and 55-58) is identical. Extracting a helper improves maintainability.
Suggested refactor
+function showErrorBadge(tabId) { + if (tabId === undefined) return; + chrome.action.setBadgeText({ text: "✖", tabId }).catch(() => {}); + chrome.action.setBadgeBackgroundColor({ color: "#EF4444", tabId }).catch(() => {}); + setTimeout(() => chrome.action.setBadgeText({ text: "", tabId }).catch(() => {}), 3000); +} + chrome.commands.onCommand.addListener(async (command) => { // ... existing code ... } else { - if (tabId !== undefined) { - chrome.action.setBadgeText({ text: "✖", tabId }).catch(() => {}); - chrome.action.setBadgeBackgroundColor({ color: "#EF4444", tabId }).catch(() => {}); - setTimeout(() => chrome.action.setBadgeText({ text: "", tabId }).catch(() => {}), 3000); - } + showErrorBadge(tabId); } } catch { - if (tabId !== undefined) { - chrome.action.setBadgeText({ text: "✖", tabId }).catch(() => {}); - chrome.action.setBadgeBackgroundColor({ color: "#EF4444", tabId }).catch(() => {}); - setTimeout(() => chrome.action.setBadgeText({ text: "", tabId }).catch(() => {}), 3000); - } + showErrorBadge(tabId); } });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extension/serviceworker.js` around lines 47 - 59, The duplicated error badge code that calls chrome.action.setBadgeText and chrome.action.setBadgeBackgroundColor and the setTimeout appears twice; extract this into a helper (e.g., showErrorBadge(tabId)) that takes tabId, performs the three operations with the same .catch handlers, and replace both duplicated blocks with a call to that helper where tabId !== undefined; update any surrounding scope so the helper can access chrome and tabId as needed.
24-25: Consider wrapping URL parsing in try-catch.
new URL(tab.url)can throwTypeErroron malformed URLs. While Chrome typically provides valid URLs, wrapping this in a try-catch adds defensive protection.Suggested diff
- const protocol = new URL(tab.url).protocol; - if (protocol !== "http:" && protocol !== "https:") return; + let protocol; + try { + protocol = new URL(tab.url).protocol; + } catch { + return; + } + if (protocol !== "http:" && protocol !== "https:") return;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extension/serviceworker.js` around lines 24 - 25, Wrap the URL parsing that uses new URL(tab.url) in a try-catch to defensively handle malformed URLs: catch the TypeError thrown by new URL(tab.url), optionally log or ignore the error, and return early instead of letting it crash; then keep the existing protocol check (protocol !== "http:" && protocol !== "https:") unchanged. Target the code around the protocol assignment (the new URL(tab.url) call) in serviceworker.js and ensure any error path exits the handler cleanly.
1-3: Remove unused constants.
SUPABASE_URLandSUPABASE_ANON_KEYare declared but never used in this file. OnlyAPI_BASEis referenced (line 30). Consider removing the dead code to reduce confusion.Suggested diff
-const SUPABASE_URL = "https://gfdwrzaeofehgpwuulvr.supabase.co"; -const SUPABASE_ANON_KEY = "sb_publishable_PLZj21S_KtFVCz1-jiq5PQ_3WNyg3dQ"; const API_BASE = "https://recallsave.vercel.app";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extension/serviceworker.js` around lines 1 - 3, SUPABASE_URL and SUPABASE_ANON_KEY are dead declarations and should be removed to avoid confusion; delete the const declarations for SUPABASE_URL and SUPABASE_ANON_KEY and keep only the API_BASE constant (used elsewhere, e.g., in the fetch logic that references API_BASE) so the file only declares the actually used symbol.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@extension/serviceworker.js`:
- Around line 47-59: The duplicated error badge code that calls
chrome.action.setBadgeText and chrome.action.setBadgeBackgroundColor and the
setTimeout appears twice; extract this into a helper (e.g.,
showErrorBadge(tabId)) that takes tabId, performs the three operations with the
same .catch handlers, and replace both duplicated blocks with a call to that
helper where tabId !== undefined; update any surrounding scope so the helper can
access chrome and tabId as needed.
- Around line 24-25: Wrap the URL parsing that uses new URL(tab.url) in a
try-catch to defensively handle malformed URLs: catch the TypeError thrown by
new URL(tab.url), optionally log or ignore the error, and return early instead
of letting it crash; then keep the existing protocol check (protocol !== "http:"
&& protocol !== "https:") unchanged. Target the code around the protocol
assignment (the new URL(tab.url) call) in serviceworker.js and ensure any error
path exits the handler cleanly.
- Around line 1-3: SUPABASE_URL and SUPABASE_ANON_KEY are dead declarations and
should be removed to avoid confusion; delete the const declarations for
SUPABASE_URL and SUPABASE_ANON_KEY and keep only the API_BASE constant (used
elsewhere, e.g., in the fetch logic that references API_BASE) so the file only
declares the actually used symbol.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 013d16cf-7c7f-400f-990a-80688aa76c68
📒 Files selected for processing (1)
extension/serviceworker.js
Summary
(
lib/gemini.ts). Tagging failures are non-fataland don't block the save.
your bookmarks →" link to
/app,Alt+Shift+Skeyboard shortcut, fixed
host_permissionsforVercel
/auth?mode=login,"Get started free" →
/auth?mode=signup?modeparam on load, explicit"← Back to home" link below the card
<img>→<Image />ESLint warningsacross page.tsx, Header, BookmarkCard
remotePatternswildcard in next.config.tsfor external OG thumbnails
npm audit fix— 0 vulnerabilities remainingextension install guide, roadmap
Done
Type of change
Checklist
npm run build)npx tsc --noEmit)npm run lint)Summary by CodeRabbit
New Features
Improvements
Documentation