Skip to content

Commit 1e2f94d

Browse files
Merge pull request #8 from BitcoinErrorLog/feature/annotation-delete-and-toggle
Add annotation delete and toggle features
2 parents bb407ec + 2d7e9c4 commit 1e2f94d

File tree

11 files changed

+360
-18
lines changed

11 files changed

+360
-18
lines changed

load-extension.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/bin/bash
2+
3+
# Script to build and prepare extension for Chrome loading
4+
5+
cd "$(dirname "$0")"
6+
7+
echo "Building extension..."
8+
npm run build
9+
10+
if [ $? -eq 0 ]; then
11+
echo ""
12+
echo "✅ Build successful!"
13+
echo ""
14+
echo "To load in Chrome:"
15+
echo "1. Open chrome://extensions/"
16+
echo "2. Enable 'Developer mode' (toggle in top right)"
17+
echo "3. Click 'Load unpacked'"
18+
echo "4. Select this folder: $(pwd)/dist"
19+
echo ""
20+
echo "Opening Chrome extensions page..."
21+
open -a "Google Chrome" "chrome://extensions/" 2>/dev/null || open -a "Chromium" "chrome://extensions/" 2>/dev/null || echo "Please open chrome://extensions/ manually"
22+
else
23+
echo "❌ Build failed. Please check the errors above."
24+
exit 1
25+
fi

package-lock.json

Lines changed: 23 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/content/AnnotationManager.ts

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,58 @@ export class AnnotationManager {
5858
this.init();
5959
}
6060

61-
private init() {
61+
private annotationsEnabled: boolean = true;
62+
63+
private async init() {
6264
logger.info('ContentScript', 'Initializing annotation manager');
6365
this.injectStyles();
6466

67+
// Load annotation enabled setting (default: true)
68+
try {
69+
const { storage } = await import('../utils/storage');
70+
const enabled = await storage.getSetting<boolean>('annotationsEnabled', true);
71+
this.annotationsEnabled = enabled ?? true;
72+
logger.info('ContentScript', 'Annotation enabled setting loaded', { enabled: this.annotationsEnabled });
73+
} catch (error) {
74+
logger.warn('ContentScript', 'Failed to load annotation setting, defaulting to enabled', error as Error);
75+
this.annotationsEnabled = true;
76+
}
77+
6578
// Bind handlers once for cleanup
6679
this.mouseUpHandler = this.handleTextSelection.bind(this);
6780
this.messageHandler = this.handleMessage.bind(this);
6881

6982
document.addEventListener('mouseup', this.mouseUpHandler);
7083
chrome.runtime.onMessage.addListener(this.messageHandler);
7184

85+
// Listen for toggle messages
86+
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
87+
if (message.type === 'TOGGLE_ANNOTATIONS') {
88+
this.annotationsEnabled = message.enabled ?? !this.annotationsEnabled;
89+
logger.info('ContentScript', 'Annotations toggled', { enabled: this.annotationsEnabled });
90+
if (!this.annotationsEnabled) {
91+
this.hideAnnotationButton();
92+
}
93+
sendResponse({ enabled: this.annotationsEnabled });
94+
return true;
95+
}
96+
if (message.type === 'GET_ANNOTATIONS_ENABLED') {
97+
sendResponse({ enabled: this.annotationsEnabled });
98+
return true;
99+
}
100+
return false;
101+
});
102+
103+
// Listen for keyboard shortcut (Alt+Shift+A)
104+
document.addEventListener('keydown', (e) => {
105+
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
106+
const altKey = isMac ? e.altKey : e.altKey;
107+
if (altKey && e.shiftKey && e.key.toLowerCase() === 'a') {
108+
e.preventDefault();
109+
this.toggleAnnotations();
110+
}
111+
});
112+
72113
// Delay initial annotation load to allow dynamic content to load (SPAs)
73114
// This is especially important for sites like pubky.app that load content asynchronously
74115
setTimeout(() => {
@@ -333,9 +374,26 @@ export class AnnotationManager {
333374
}
334375

335376
private handleTextSelection(event: MouseEvent) {
377+
// Don't process if annotations are disabled
378+
if (!this.annotationsEnabled) {
379+
return;
380+
}
381+
382+
// Don't hide button if clicking on the annotation button itself
383+
const target = event.target as HTMLElement;
384+
if (target?.closest('.pubky-annotation-button') || target?.closest('.pubky-annotation-modal')) {
385+
return;
386+
}
387+
336388
const selection = window.getSelection();
337389
if (!selection || selection.isCollapsed) {
338-
this.hideAnnotationButton();
390+
// Delay hiding to allow button click to register
391+
setTimeout(() => {
392+
const button = document.querySelector('.pubky-annotation-button');
393+
if (button && !button.contains(document.activeElement)) {
394+
this.hideAnnotationButton();
395+
}
396+
}, 100);
339397
return;
340398
}
341399

@@ -366,10 +424,25 @@ export class AnnotationManager {
366424
`;
367425
button.style.left = `${x - 80}px`;
368426
button.style.top = `${y + 10}px`;
427+
428+
// Use both mousedown and click to ensure the modal opens
429+
// mousedown fires before mouseup, so it prevents the button from being hidden
369430
button.onmousedown = (e) => {
431+
e.preventDefault();
370432
e.stopPropagation();
433+
// Show modal immediately on mousedown to prevent button from being hidden
371434
this.showAnnotationModal();
372435
};
436+
437+
// Also handle click as backup
438+
button.onclick = (e) => {
439+
e.preventDefault();
440+
e.stopPropagation();
441+
// If modal isn't already showing, show it
442+
if (!document.querySelector('.pubky-annotation-modal')) {
443+
this.showAnnotationModal();
444+
}
445+
};
373446

374447
document.body.appendChild(button);
375448
}
@@ -381,6 +454,32 @@ export class AnnotationManager {
381454
}
382455
}
383456

457+
private async toggleAnnotations() {
458+
const newValue = !this.annotationsEnabled;
459+
this.annotationsEnabled = newValue;
460+
461+
try {
462+
const { storage } = await import('../utils/storage');
463+
await storage.saveSetting('annotationsEnabled', newValue);
464+
logger.info('ContentScript', 'Annotations toggled via keyboard', { enabled: newValue });
465+
466+
// Show visual feedback
467+
chrome.runtime.sendMessage({
468+
type: MESSAGE_TYPES.SHOW_TOAST,
469+
toastType: newValue ? 'success' : 'info',
470+
message: newValue ? 'Annotations enabled' : 'Annotations disabled',
471+
}).catch(() => {
472+
// Ignore if background script not available
473+
});
474+
475+
if (!newValue) {
476+
this.hideAnnotationButton();
477+
}
478+
} catch (error) {
479+
logger.error('ContentScript', 'Failed to toggle annotations', error as Error);
480+
}
481+
}
482+
384483
private showAnnotationModal() {
385484
if (!this.currentSelection) return;
386485
this.hideAnnotationButton();
@@ -789,6 +888,24 @@ export class AnnotationManager {
789888
}
790889
}
791890
sendResponse({ success: true });
891+
} else if (message.type === MESSAGE_TYPES.REMOVE_ANNOTATION) {
892+
const annotationId = message.annotationId;
893+
// Remove highlight from DOM
894+
const highlight = document.querySelector(`[data-annotation-id="${annotationId}"]`);
895+
if (highlight) {
896+
// Unwrap the highlight span, restoring original text
897+
const parent = highlight.parentNode;
898+
if (parent) {
899+
while (highlight.firstChild) {
900+
parent.insertBefore(highlight.firstChild, highlight);
901+
}
902+
parent.removeChild(highlight);
903+
logger.info('ContentScript', 'Annotation highlight removed from page', { id: annotationId });
904+
}
905+
}
906+
// Remove from local annotations array
907+
this.annotations = this.annotations.filter((a) => a.id !== annotationId);
908+
sendResponse({ success: true });
792909
}
793910
return true;
794911
}

src/offscreen/offscreen.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
1616
This document is never visible to users.
1717
-->
18-
<script type="module" src="offscreen.js"></script>
18+
<script type="module" src="/src/offscreen/offscreen.ts"></script>
1919
</body>
2020
</html>
2121

src/popup/components/KeyboardShortcutsModal.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ function KeyboardShortcutsModal({ isOpen, onClose }: KeyboardShortcutsModalProps
4343
description: 'Open annotations tab',
4444
category: 'Navigation'
4545
},
46+
{
47+
keys: [`${modifierKey}+Shift+A`],
48+
description: 'Toggle annotation button (enable/disable)',
49+
category: 'Actions'
50+
},
4651
{
4752
keys: ['Shift+?'],
4853
description: 'Show keyboard shortcuts',

0 commit comments

Comments
 (0)