Skip to content

[new feature] show the last 4 letters of the API key#129

Open
jovezhong wants to merge 3 commits intosohzm:masterfrom
jovezhong:feat/showapikey
Open

[new feature] show the last 4 letters of the API key#129
jovezhong wants to merge 3 commits intosohzm:masterfrom
jovezhong:feat/showapikey

Conversation

@jovezhong
Copy link

@jovezhong jovezhong commented Aug 31, 2025

Close #128

Add a new button on the launch screen to show a popup with last 4 letter of the api key. Helpful when you have many Gemini keys and want to confirm which key is set without showing the entire api key

Summary by CodeRabbit

  • New Features

    • Added API key identifier popup displaying the last four characters of your API key via a new eye icon button next to the input field.
    • Added onboarding reset functionality to remove saved preferences and reload the app.
  • Tests

    • Added comprehensive test suite covering API key logic and popup state transitions.

✏️ Tip: You can customize this high-level summary in your review settings.

jovezhong and others added 2 commits August 30, 2025 21:11
- Replace lock icon with eye icon (👁️) for better UX
- Add descriptive tooltip for the show key button
- Move "Got it" button to bottom right corner for standard positioning
- Remove redundant X close button to simplify UI
- Improve popup layout with centered title and organized bottom section
- Enhance visual consistency with existing app styling

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@jovezhong jovezhong changed the title [new feature [new feature] show the last 4 letter of the API key Aug 31, 2025
@jovezhong jovezhong changed the title [new feature] show the last 4 letter of the API key [new feature] show the last 4 letters of the API key Aug 31, 2025
@Kanishk1420
Copy link

Bro can you work on the feature which will copy the latest response well somehow that feature in software is not working well?

@jovezhong
Copy link
Author

Hi @Kanishk1420 , I submitted a PR to export the texts in the session as markdown #124

I don't really use this tool to copy "real-time" response, so I am not aware of the bug or how to improve it.

As you see, this PR is solely built by Claude Code, you just need an idea, let it working it and give some feedbacks to refine it. You can try to fix the issue you mentioned in that way. Good luck.

@Kanishk1420
Copy link

Kanishk1420 commented Sep 1, 2025

@jovezhong hey i have tried your pr #124 in my local well but didnt understood what your pr actually did in the software and work could you explain me .well and im trying to make this feature with github copilot with claude opus 4.1 model but it cant able to make it properly its always making dumb mistakes so yea thats why i need help.

@jovezhong
Copy link
Author

We can have the discussion in #124 In the session list page, you can export all of them or one session as markdown. I would recommend Claude Code CLI, instead of GitHub Copilot for such new feature building.

@coderabbitai
Copy link

coderabbitai bot commented Dec 21, 2025

Walkthrough

This pull request introduces an API key identifier feature with a popup dialog. A new test suite validates the popup state management and last-four-letters extraction logic. The MainView component now includes an eye-icon button to display the last four characters of a stored API key in a centered modal overlay, along with utility methods for popup management and onboarding reset.

Changes

Cohort / File(s) Summary
New API Key Identifier Tests
src/__tests__/mainView.test.js
Introduces comprehensive test suite for MainView API key logic, including mocked localStorage and tests for getLastFourLetters extraction, popup state transitions, and edge cases with varying API key lengths.
MainView Component API Key Identifier Feature
src/components/views/MainView.js
Adds eye-icon button triggering a full-screen overlay popup displaying the last 4 characters of the API key. Implements public methods for popup open/close handlers, last-four-letters extraction from localStorage, layout mode loading, and onboarding reset. Includes new state property showApiKeyPopup and corresponding UI markup with styling.

Sequence Diagram

sequenceDiagram
    actor User
    participant MainView
    participant localStorage
    participant Popup UI

    User->>MainView: Click eye-icon button
    MainView->>MainView: handleApiKeyButtonClick()
    MainView->>MainView: getLastFourLetters()
    MainView->>localStorage: Read apiKey
    localStorage-->>MainView: Return apiKey value
    MainView->>MainView: Extract last 4 characters
    MainView->>Popup UI: Set showApiKeyPopup = true
    Popup UI->>User: Display overlay & popup with last 4 letters
    
    alt User closes via "Got it" button
        User->>MainView: Click dismiss button
        MainView->>MainView: handleCloseApiKeyPopup()
    else User closes via overlay
        User->>Popup UI: Click overlay
        MainView->>MainView: handleCloseApiKeyPopup()
    end
    
    MainView->>Popup UI: Set showApiKeyPopup = false
    Popup UI->>User: Hide overlay & popup
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Areas requiring attention:

  • Verification of getLastFourLetters logic for edge cases (keys < 4 characters, null/undefined values)
  • Popup overlay click propagation and event handler isolation to ensure only overlay clicks trigger close
  • localStorage key naming consistency with other parts of the codebase
  • Accessibility of eye-icon button (tooltip, ARIA labels, keyboard navigation)
  • CSS styling and transitions for smooth popup show/hide animations

Poem

🐰 A key's last whisper, hidden four,
Behind an eye that sees it all,
No secrets spilled, just four letters more,
Guarding accounts behind the wall! 👁️

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main feature added: displaying the last 4 letters of the API key in a popup.
Linked Issues check ✅ Passed The PR implements all core requirements from issue #128: eye icon button, popup showing last 4 characters, 'Got it' button, tooltip, backdrop close, edge case handling, and localStorage integration.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the API key identifier feature. The addition of handleResetOnboarding and loadLayoutMode appear to be pre-existing or supporting functionality without breaking changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
src/__tests__/mainView.test.js (1)

20-40: Tests duplicate implementation logic instead of testing the actual component.

The mock object reimplements getLastFourLetters() rather than testing the actual MainView implementation. If the real implementation diverges, these tests will still pass while the component is broken.

Consider either:

  1. Importing and testing the actual MainView component with appropriate DOM mocking
  2. Extracting getLastFourLetters to a utility module that can be tested directly
src/components/views/MainView.js (2)

391-412: Add keyboard accessibility for popup dismissal.

The popup can only be closed via mouse click. Add an Escape key handler to improve keyboard accessibility, which aligns with the modal requirements in issue #128.

🔎 Proposed fix

Add a keydown handler in handleApiKeyButtonClick:

     handleApiKeyButtonClick() {
         this.showApiKeyPopup = true;
+        // Add escape key listener
+        this._escapeHandler = (e) => {
+            if (e.key === 'Escape') {
+                this.handleCloseApiKeyPopup();
+            }
+        };
+        document.addEventListener('keydown', this._escapeHandler);
     }

     handleCloseApiKeyPopup() {
         this.showApiKeyPopup = false;
+        if (this._escapeHandler) {
+            document.removeEventListener('keydown', this._escapeHandler);
+            this._escapeHandler = null;
+        }
     }

379-381: Consider using an SVG icon instead of emoji.

The emoji 👁️ may render inconsistently across different operating systems and browsers. Using an SVG icon would provide consistent appearance and better accessibility.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae5ff68 and 40e742b.

📒 Files selected for processing (2)
  • src/__tests__/mainView.test.js (1 hunks)
  • src/components/views/MainView.js (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/__tests__/mainView.test.js (1)
src/utils/gemini.js (1)
  • value (161-176)
src/components/views/MainView.js (4)
src/utils/windowResize.js (1)
  • window (4-4)
src/utils/gemini.js (1)
  • apiKey (538-538)
src/utils/renderer.js (1)
  • apiKey (137-137)
src/assets/lit-core-2.7.4.min.js (1)
  • e (16-16)
🪛 Gitleaks (8.30.0)
src/__tests__/mainView.test.js

[high] 63-63: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 112-112: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (4)
src/__tests__/mainView.test.js (1)

63-63: Static analysis false positives for test data.

The flagged strings are clearly fake test values, not real API keys. No action needed, but you could make them more obviously fake (e.g., 'TEST_KEY_abcd1234') to avoid future false alarms.

Also applies to: 112-112

src/components/views/MainView.js (3)

118-152: LGTM on popup CSS styling.

The overlay and popup styles implement the modal requirements from issue #128: centered positioning, backdrop dismissal support, and smooth transitions.


328-334: Popup open/close logic and UI are correctly implemented.

The handlers properly toggle showApiKeyPopup, and the template correctly renders the popup conditionally with backdrop click-to-close and stopPropagation() on the popup content.

Also applies to: 392-412


322-326: handleResetOnboarding() is dead code with no UI trigger.

This method at lines 322-326 is defined but never called anywhere in the codebase and has no event binding in the template. Either remove it or intentionally expose it for external use with proper UI binding (like the other handler methods handleStartClick, handleAPIKeyHelpClick, etc.).

Comment on lines +1 to +8
// Mock localStorage for Node.js environment
const storage = {};
global.localStorage = {
getItem: vi.fn((key) => storage[key] || null),
setItem: vi.fn((key, value) => { storage[key] = value; }),
removeItem: vi.fn((key) => { delete storage[key]; }),
clear: vi.fn(() => { Object.keys(storage).forEach(key => delete storage[key]); }),
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Missing import statement for Vitest utilities.

The file uses vi.fn(), describe, it, expect, and beforeEach without importing them. Add the required import at the top of the file.

🔎 Proposed fix
+import { vi, describe, it, expect, beforeEach } from 'vitest';
+
 // Mock localStorage for Node.js environment
 const storage = {};
 global.localStorage = {
📝 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.

Suggested change
// Mock localStorage for Node.js environment
const storage = {};
global.localStorage = {
getItem: vi.fn((key) => storage[key] || null),
setItem: vi.fn((key, value) => { storage[key] = value; }),
removeItem: vi.fn((key) => { delete storage[key]; }),
clear: vi.fn(() => { Object.keys(storage).forEach(key => delete storage[key]); }),
};
import { vi, describe, it, expect, beforeEach } from 'vitest';
// Mock localStorage for Node.js environment
const storage = {};
global.localStorage = {
getItem: vi.fn((key) => storage[key] || null),
setItem: vi.fn((key, value) => { storage[key] = value; }),
removeItem: vi.fn((key) => { delete storage[key]; }),
clear: vi.fn(() => { Object.keys(storage).forEach(key => delete storage[key]); }),
};
🤖 Prompt for AI Agents
In src/__tests__/mainView.test.js lines 1 to 8, the test file uses Vitest
globals (vi, describe, it, expect, beforeEach) but does not import them; add a
top-of-file import from 'vitest' that destructures vi, describe, it, expect, and
beforeEach so those functions are defined in the Node test environment before
the localStorage mock.

Comment on lines +43 to +48
it('shows "None" when no API key is set', () => {
// Mock localStorage.getItem returning null (no API key)
localStorage.getItem.mockReturnValue(null);
const lastFourLetters = mainView.getLastFourLetters();
expect(lastFourLetters).toBe('None');
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Mock pollution across tests.

Calling mockReturnValue(null) permanently overrides the mock's default implementation. Subsequent tests that use localStorage.setItem() followed by getItem() will still receive null instead of the stored value, causing test failures or false passes.

Either reset mocks in beforeEach with vi.restoreAllMocks() or use mockReturnValueOnce() for single-use overrides.

🔎 Proposed fix
 beforeEach(() => {
     // Clear localStorage before each test
     localStorage.clear();
+    // Reset mock implementations
+    vi.restoreAllMocks();
     
     // Create a mock MainView instance with just the methods we need to test

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/__tests__/mainView.test.js around lines 43 to 48, the test permanently
overrides the localStorage.getItem mock with mockReturnValue(null), causing mock
pollution across tests; change the test to use mockReturnValueOnce(null) for a
single-call override OR add a test-suite setup (e.g., in beforeEach) that
resets/restores mocks (vi.restoreAllMocks() or vi.resetAllMocks()) so later
tests using localStorage.setItem/getItem behave correctly; ensure any chosen
reset happens before each test to avoid side effects.

Comment on lines +62 to +66
it('shows last 4 letters when API key has more than 4 characters', () => {
localStorage.setItem('apiKey', 'AIzaSyB1abc2def3ghi4jkl5mno6pqr7stu8vwx9yz');
const lastFourLetters = mainView.getLastFourLetters();
expect(lastFourLetters).toBe('yz');
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Incorrect expected value in test assertion.

The API key string ends with ...vwx9yz, so slice(-4) returns '9xyz' (4 characters), not 'yz' (2 characters). This test will fail.

🔎 Proposed fix
     it('shows last 4 letters when API key has more than 4 characters', () => {
         localStorage.setItem('apiKey', 'AIzaSyB1abc2def3ghi4jkl5mno6pqr7stu8vwx9yz');
         const lastFourLetters = mainView.getLastFourLetters();
-        expect(lastFourLetters).toBe('yz');
+        expect(lastFourLetters).toBe('x9yz');
     });
📝 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.

Suggested change
it('shows last 4 letters when API key has more than 4 characters', () => {
localStorage.setItem('apiKey', 'AIzaSyB1abc2def3ghi4jkl5mno6pqr7stu8vwx9yz');
const lastFourLetters = mainView.getLastFourLetters();
expect(lastFourLetters).toBe('yz');
});
it('shows last 4 letters when API key has more than 4 characters', () => {
localStorage.setItem('apiKey', 'AIzaSyB1abc2def3ghi4jkl5mno6pqr7stu8vwx9yz');
const lastFourLetters = mainView.getLastFourLetters();
expect(lastFourLetters).toBe('x9yz');
});
🧰 Tools
🪛 Gitleaks (8.30.0)

[high] 63-63: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🤖 Prompt for AI Agents
In src/__tests__/mainView.test.js around lines 62 to 66, the test sets the
apiKey to 'AIzaSyB1abc2def3ghi4jkl5mno6pqr7stu8vwx9yz' but asserts the wrong
expected value; slice(-4) on that string returns the last four characters
("x9yz"), not "yz". Update the test assertion to expect "x9yz" (or change the
fixture to a key whose last four chars match the current expectation) so the
test matches the actual getLastFourLetters() behavior.

Comment on lines +336 to +342
getLastFourLetters() {
const apiKey = localStorage.getItem('apiKey');
if (!apiKey || apiKey.length < 4) {
return 'None';
}
return apiKey.slice(-4);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Inconsistent API key access pattern.

This method reads the API key directly from localStorage.getItem('apiKey'), but elsewhere in this file (line 268) and throughout the codebase, the key is accessed via cheatingDaddy.storage.getApiKey(). This inconsistency could cause bugs if the storage abstraction evolves.

🔎 Proposed fix
-    getLastFourLetters() {
-        const apiKey = localStorage.getItem('apiKey');
+    async getLastFourLetters() {
+        const apiKey = await cheatingDaddy.storage.getApiKey();
         if (!apiKey || apiKey.length < 4) {
             return 'None';
         }
         return apiKey.slice(-4);
     }

Note: If you make this async, you'll need to update the popup rendering to cache the value when opened rather than calling getLastFourLetters() directly in the template.

📝 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.

Suggested change
getLastFourLetters() {
const apiKey = localStorage.getItem('apiKey');
if (!apiKey || apiKey.length < 4) {
return 'None';
}
return apiKey.slice(-4);
}
getLastFourLetters() {
const apiKey = cheatingDaddy.storage.getApiKey();
if (!apiKey || apiKey.length < 4) {
return 'None';
}
return apiKey.slice(-4);
}
🤖 Prompt for AI Agents
In src/components/views/MainView.js around lines 336 to 342, getLastFourLetters
reads the API key directly from localStorage which is inconsistent with the rest
of the file and codebase; change it to call cheatingDaddy.storage.getApiKey()
(respecting its current sync signature), perform the same null/length check and
.slice(-4) on that value, and ensure you do not convert the method to async
unless you also update the popup rendering to cache the value when opened.

Comment on lines +344 to +350
loadLayoutMode() {
const savedLayoutMode = localStorage.getItem('layoutMode');
if (savedLayoutMode && savedLayoutMode !== 'normal') {
// Notify parent component to apply the saved layout mode
this.onLayoutModeChange(savedLayoutMode);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n 'loadLayoutMode' --type=js

Repository: sohzm/cheating-daddy

Length of output: 123


Remove loadLayoutMode() or call it from connectedCallback().

The method is defined but never invoked in the codebase. If it's not needed, remove it; if it should run during initialization, add a call to this.loadLayoutMode() in connectedCallback().

🤖 Prompt for AI Agents
In src/components/views/MainView.js around lines 344 to 350, the
loadLayoutMode() method is defined but never used; either remove the method if
it's unnecessary, or invoke it from the component initialization by adding a
call to this.loadLayoutMode() inside connectedCallback() (ensure
connectedCallback() calls super.connectedCallback() first if applicable) so the
saved layout is applied on mount; update tests/usage accordingly and remove dead
code if you choose deletion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add API key identifier feature with last 4 letters popup

2 participants