feat: LEXAI-13 API encryption, LEXAI-11 context menu, LEXAI-12 tone analysis
- LEXAI-13: TweetNaCl secretbox encryption for API keys in Options + background
- getOrCreateEncKey() generates per-device 32-byte key stored in chrome.storage.local
- Keys saved as nonce+ciphertext (base64) under apiKeyEnc
- Backward compat: falls back to plaintext apiKey if no encrypted key found
- Lock icon 🔒 shown in Options label when key is encrypted
- LEXAI-11: Right-click context menu with Fix, Rephrase, Shorten, Expand, Tone
- Registered via onInstalled in background service worker
- Sends lexai-context-menu message to content script
- Content script listener added at bottom of main()
- LEXAI-12: Tone analysis button added to floating toolbar (teal #2dd4bf)
- Analysis-only: showModal called with null originalText = no Replace button
- Tone system prompt updated to pure analysis (no rewrite)
This commit is contained in:
@@ -186,6 +186,7 @@ export default defineContentScript({
|
||||
{ label: '↺ Rephrase', action: 'rephrase', color: '#89b4fa' },
|
||||
{ label: '↓ Shorten', action: 'shorten', color: '#fab387' },
|
||||
{ label: '↑ Expand', action: 'expand', color: '#cba6f7' },
|
||||
{ label: '🎭 Tone', action: 'tone', color: '#2dd4bf' },
|
||||
];
|
||||
|
||||
actions.forEach(({ label, action, color }) => {
|
||||
@@ -340,7 +341,13 @@ export default defineContentScript({
|
||||
if (response?.error) {
|
||||
showModal(`❌ ${response.error}`, null, snapStart, snapEnd, snapElement, snapRange);
|
||||
} else {
|
||||
showModal(response?.result ?? '(no result)', textToProcess, snapStart, snapEnd, snapElement, snapRange);
|
||||
// Tone analysis is display-only — no Replace button (pass null for originalText)
|
||||
const isToneAction = action === 'tone';
|
||||
showModal(
|
||||
response?.result ?? '(no result)',
|
||||
isToneAction ? null : textToProcess,
|
||||
snapStart, snapEnd, snapElement, snapRange,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
hideToolbar();
|
||||
@@ -653,5 +660,18 @@ export default defineContentScript({
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Context menu trigger from background ─────────────────────────────────
|
||||
chrome.runtime.onMessage.addListener((message) => {
|
||||
if (message.type === 'lexai-context-menu') {
|
||||
selectedText = message.text as string;
|
||||
// For context menu, we have no DOM selection positions — zero them out
|
||||
storedStart = -1;
|
||||
storedEnd = -1;
|
||||
storedElement = null;
|
||||
storedRange = null;
|
||||
runAction(message.action as string);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user