Address maintainer feedback: Clean up documentation and remove development files

- Improve README.md formatting and spacing
- Remove development/debugging files (MANIFEST_V3_CONVERSION.md, PULL_REQUEST.md, background-v2.js, verify-conversion.sh)
- Clean up project structure as requested in PR review
This commit is contained in:
Octech2722 2025-10-06 15:27:33 -05:00
parent 10d8437d73
commit 0c1de7e183
5 changed files with 5 additions and 803 deletions

View File

@ -1,144 +0,0 @@
# Trilium Web Clipper - Manifest V3 Conversion Summary
## ✅ Completed Conversion Tasks
### 1. **Manifest.json Updates**
- ✅ Updated `manifest_version` from 2 to 3
- ✅ Converted `browser_action` to `action`
- ✅ Updated `background.scripts` to `background.service_worker` with ES module support
- ✅ Separated `permissions` and `host_permissions`
- ✅ Added `scripting` permission for dynamic content script injection
- ✅ Updated `content_security_policy` to V3 format
- ✅ Added `web_accessible_resources` with proper structure
- ✅ Removed static `content_scripts` (now using dynamic injection)
### 2. **Background Script Conversion**
- ✅ Converted from background.js to ES module service worker
- ✅ Replaced all `browser.*` API calls with `chrome.*`
- ✅ Converted `browser.browserAction` to `chrome.action`
- ✅ Updated `browser.tabs.executeScript` to `chrome.scripting.executeScript`
- ✅ Added dynamic content script injection with error handling
- ✅ Updated message listener to return `true` for async responses
- ✅ Converted utility and facade imports to ES modules
### 3. **Utils.js ES Module Conversion**
- ✅ Added `export` statements for all functions
- ✅ Maintained backward compatibility
### 4. **Trilium Server Facade Conversion**
- ✅ Replaced all `browser.*` calls with `chrome.*`
- ✅ Added proper ES module exports
- ✅ Updated storage and runtime message APIs
### 5. **Content Script Updates**
- ✅ Replaced all `browser.*` calls with `chrome.*`
- ✅ Added inline utility functions to avoid module dependency issues
- ✅ Maintained compatibility with dynamic library loading
### 6. **Popup and Options Scripts**
- ✅ Updated all `browser.*` API calls to `chrome.*`
- ✅ Updated storage, runtime, and other extension APIs
## 🔧 Key Technical Changes
### Dynamic Content Script Injection
Instead of static registration, content scripts are now injected on-demand:
```javascript
await chrome.scripting.executeScript({
target: { tabId: activeTab.id },
files: ['content.js']
});
```
### ES Module Service Worker
Background script now uses ES modules:
```javascript
import { randomString } from './utils.js';
import { triliumServerFacade } from './trilium_server_facade.js';
```
### Chrome APIs Everywhere
All `browser.*` calls replaced with `chrome.*`:
- `browser.tabs``chrome.tabs`
- `browser.storage``chrome.storage`
- `browser.runtime``chrome.runtime`
- `browser.contextMenus``chrome.contextMenus`
### Host Permissions Separation
```json
{
"permissions": ["activeTab", "tabs", "storage", "contextMenus", "scripting"],
"host_permissions": ["http://*/", "https://*/"]
}
```
## 🧪 Testing Checklist
### Basic Functionality
- [ ] Extension loads without errors
- [ ] Popup opens and displays correctly
- [ ] Options page opens and functions
- [ ] Context menus appear on right-click
### Core Features
- [ ] Save selection to Trilium
- [ ] Save whole page to Trilium
- [ ] Save screenshots to Trilium
- [ ] Save images to Trilium
- [ ] Save links to Trilium
- [ ] Keyboard shortcuts work
### Integration
- [ ] Trilium Desktop connection works
- [ ] Trilium Server connection works
- [ ] Toast notifications appear
- [ ] Note opening in Trilium works
## 📝 Migration Notes
### Files Changed
- `manifest.json` - Complete V3 conversion
- `background.js` - New ES module service worker
- `utils.js` - ES module exports added
- `trilium_server_facade.js` - Chrome APIs + ES exports
- `content.js` - Chrome APIs + inline utilities
- `popup/popup.js` - Chrome APIs
- `options/options.js` - Chrome APIs
### Files Preserved
- `background-v2.js` - Original V2 background (backup)
- All library files in `/lib/` unchanged
- All UI files (HTML/CSS) unchanged
- Icons and other assets unchanged
### Breaking Changes
- Browser polyfill no longer needed for Chrome extension
- Content scripts loaded dynamically (better for performance)
- Service worker lifecycle different from persistent background
## 🚀 Next Steps
1. Load extension in Chrome developer mode
2. Test all core functionality
3. Verify Trilium Desktop/Server integration
4. Test keyboard shortcuts
5. Verify error handling and edge cases

View File

@ -1,128 +0,0 @@
# Trilium Web Clipper - Manifest V3 Conversion
## 📋 **Summary**
This pull request upgrades the Trilium Web Clipper Chrome extension from Manifest V2 to Manifest V3, ensuring compatibility with Chrome's future extension platform while adding significant UX improvements.
## ✨ **Key Improvements**
### **🚀 Performance Enhancements**
- **Faster page saving** - Optimized async operations eliminate blocking
- **Smart content script injection** - Only injects when needed, reducing overhead
- **Efficient error handling** - Clean fallback mechanisms
### **👤 Better User Experience**
- **Progressive status notifications** - Real-time feedback with emojis:
- 📄 "Page capture started..."
- 🖼️ "Processing X image(s)..."
- 💾 "Saving to Trilium Desktop/Server..."
- ✅ "Page has been saved to Trilium." (with clickable link)
- **Instant feedback** - No more wondering "is it working?"
- **Error-free operation** - Clean console logs
## 🔧 **Technical Changes**
### **Manifest V3 Compliance**
- Updated `manifest_version` from 2 to 3
- Converted `browser_action``action`
- Updated `background` scripts → `service_worker` with ES modules
- Separated `permissions` and `host_permissions`
- Added `scripting` permission for dynamic injection
- Updated `content_security_policy` to V3 format
### **API Modernization**
- Replaced all `browser.*` calls with `chrome.*` APIs
- Updated `browser.tabs.executeScript``chrome.scripting.executeScript`
- Converted to ES module architecture
- Added proper async message handling
### **Architecture Improvements**
- **Service Worker Background Script** - Modern persistent background
- **Dynamic Content Script Injection** - Better performance and reliability
- **ES Module System** - Cleaner imports/exports throughout
- **Robust Error Handling** - Graceful degradation on failures
## 📁 **Files Modified**
### Core Extension Files
- `manifest.json` - Complete V3 conversion
- `background.js` - New ES module service worker
- `content.js` - Chrome APIs + enhanced messaging
- `utils.js` - ES module exports
- `trilium_server_facade.js` - Chrome APIs + ES exports
### UI Scripts
- `popup/popup.js` - Chrome API updates
- `options/options.js` - Chrome API updates
### Backup Files Created
- `background-v2.js` - Original V2 background (preserved)
## 🧪 **Testing Completed**
### ✅ **Core Functionality**
- Extension loads without errors
- All save operations work (selection, page, screenshots, images, links)
- Context menus and keyboard shortcuts functional
- Popup and options pages working
### ✅ **Integration Testing**
- Trilium Desktop connection verified
- Trilium Server connection verified
- Toast notifications with clickable links working
- Note opening in Trilium verified
### ✅ **Performance Testing**
- Faster save operations confirmed
- Clean error-free console logs
- Progressive status updates working
## 🔄 **Migration Path**
### **Backward Compatibility**
- All existing functionality preserved
- No breaking changes to user experience
- Original V2 code backed up as `background-v2.js`
### **Future Readiness**
- Compatible with Chrome Manifest V3 requirements
- Prepared for Manifest V2 deprecation (June 2024)
- Modern extension architecture
## 🎯 **Benefits for Users**
1. **Immediate** - Better feedback during save operations
2. **Future-proof** - Will continue working as Chrome evolves
3. **Faster** - Optimized performance improvements
4. **Reliable** - Enhanced error handling and recovery
## 📝 **Notes for Reviewers**
- This maintains 100% functional compatibility with existing extension
- ES modules provide better code organization and maintainability
- Progressive status system significantly improves user experience
- All chrome.* APIs are stable and recommended for V3
## 🧹 **Clean Implementation**
- No deprecated APIs used
- Follows Chrome extension best practices
- Comprehensive error handling
- Clean separation of concerns with ES modules
---
**Ready for production use** - Extensively tested and verified working with both Trilium Desktop and Server configurations.

View File

@ -4,12 +4,14 @@
**Trilium is in maintenance mode and Web Clipper is not likely to get new releases.**
Trilium Web Clipper is a web browser extension which allows user to clip text, screenshots, whole pages and short notes and save them directly to [Trilium Notes](https://github.com/zadam/trilium).
Trilium Web Clipper is a web browser extension which allows user to clip text, screenshots, whole pages and short notes and save them directly to [Trilium Notes](https://github.com/zadam/trilium).
For more details, see the [wiki page](https://github.com/zadam/trilium/wiki/Web-clipper).
## Keyboard shortcuts
Keyboard shortcuts are available for most functions:
Keyboard shortcuts are available for most functions:
* Save selected text: `Ctrl+Shift+S` (Mac: `Cmd+Shift+S`)
* Save whole page: `Alt+Shift+S` (Mac: `Opt+Shift+S`)
* Save screenshot: `Ctrl+Shift+E` (Mac: `Cmd+Shift+E`)
@ -21,4 +23,5 @@ To set custom shortcuts, follow the directions for your browser.
**Chrome**: `chrome://extensions/shortcuts`
## Credits
Some parts of the code are based on the [Joplin Notes browser extension](https://github.com/laurent22/joplin/tree/master/Clipper).

View File

@ -1,451 +0,0 @@
// Keyboard shortcuts
chrome.commands.onCommand.addListener(async function (command) {
if (command == "saveSelection") {
await saveSelection();
} else if (command == "saveWholePage") {
await saveWholePage();
} else if (command == "saveTabs") {
await saveTabs();
} else if (command == "saveCroppedScreenshot") {
const activeTab = await getActiveTab();
await saveCroppedScreenshot(activeTab.url);
} else {
console.log("Unrecognized command", command);
}
});
function cropImage(newArea, dataUrl) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function () {
const canvas = document.createElement('canvas');
canvas.width = newArea.width;
canvas.height = newArea.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, newArea.x, newArea.y, newArea.width, newArea.height, 0, 0, newArea.width, newArea.height);
resolve(canvas.toDataURL());
};
img.src = dataUrl;
});
}
async function takeCroppedScreenshot(cropRect) {
const activeTab = await getActiveTab();
const zoom = await browser.tabs.getZoom(activeTab.id) * window.devicePixelRatio;
const newArea = Object.assign({}, cropRect);
newArea.x *= zoom;
newArea.y *= zoom;
newArea.width *= zoom;
newArea.height *= zoom;
const dataUrl = await browser.tabs.captureVisibleTab(null, { format: 'png' });
return await cropImage(newArea, dataUrl);
}
async function takeWholeScreenshot() {
// this saves only visible portion of the page
// workaround to save the whole page is to scroll & stitch
// example in https://github.com/mrcoles/full-page-screen-capture-chrome-extension
// see page.js and popup.js
return await browser.tabs.captureVisibleTab(null, { format: 'png' });
}
browser.runtime.onInstalled.addListener(() => {
if (isDevEnv()) {
browser.browserAction.setIcon({
path: 'icons/32-dev.png',
});
}
});
browser.contextMenus.create({
id: "trilium-save-selection",
title: "Save selection to Trilium",
contexts: ["selection"]
});
browser.contextMenus.create({
id: "trilium-save-cropped-screenshot",
title: "Clip screenshot to Trilium",
contexts: ["page"]
});
browser.contextMenus.create({
id: "trilium-save-cropped-screenshot",
title: "Crop screen shot to Trilium",
contexts: ["page"]
});
browser.contextMenus.create({
id: "trilium-save-whole-screenshot",
title: "Save whole screen shot to Trilium",
contexts: ["page"]
});
browser.contextMenus.create({
id: "trilium-save-page",
title: "Save whole page to Trilium",
contexts: ["page"]
});
browser.contextMenus.create({
id: "trilium-save-link",
title: "Save link to Trilium",
contexts: ["link"]
});
browser.contextMenus.create({
id: "trilium-save-image",
title: "Save image to Trilium",
contexts: ["image"]
});
async function getActiveTab() {
const tabs = await browser.tabs.query({
active: true,
currentWindow: true
});
return tabs[0];
}
async function getWindowTabs() {
const tabs = await browser.tabs.query({
currentWindow: true
});
return tabs;
}
async function sendMessageToActiveTab(message) {
const activeTab = await getActiveTab();
if (!activeTab) {
throw new Error("No active tab.");
}
try {
return await browser.tabs.sendMessage(activeTab.id, message);
}
catch (e) {
throw e;
}
}
function toast(message, noteId = null, tabIds = null) {
sendMessageToActiveTab({
name: 'toast',
message: message,
noteId: noteId,
tabIds: tabIds
});
}
function blob2base64(blob) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onloadend = function() {
resolve(reader.result);
};
reader.readAsDataURL(blob);
});
}
async function fetchImage(url) {
const resp = await fetch(url);
const blob = await resp.blob();
return await blob2base64(blob);
}
async function postProcessImage(image) {
if (image.src.startsWith("data:image/")) {
image.dataUrl = image.src;
image.src = "inline." + image.src.substr(11, 3); // this should extract file type - png/jpg
}
else {
try {
image.dataUrl = await fetchImage(image.src, image);
}
catch (e) {
console.log(`Cannot fetch image from ${image.src}`);
}
}
}
async function postProcessImages(resp) {
if (resp.images) {
for (const image of resp.images) {
await postProcessImage(image);
}
}
}
async function saveSelection() {
const payload = await sendMessageToActiveTab({name: 'trilium-save-selection'});
await postProcessImages(payload);
const resp = await triliumServerFacade.callService('POST', 'clippings', payload);
if (!resp) {
return;
}
toast("Selection has been saved to Trilium.", resp.noteId);
}
async function getImagePayloadFromSrc(src, pageUrl) {
const image = {
imageId: randomString(20),
src: src
};
await postProcessImage(image);
const activeTab = await getActiveTab();
return {
title: activeTab.title,
content: `<img src="${image.imageId}">`,
images: [image],
pageUrl: pageUrl
};
}
async function saveCroppedScreenshot(pageUrl) {
const cropRect = await sendMessageToActiveTab({name: 'trilium-get-rectangle-for-screenshot'});
const src = await takeCroppedScreenshot(cropRect);
const payload = await getImagePayloadFromSrc(src, pageUrl);
const resp = await triliumServerFacade.callService("POST", "clippings", payload);
if (!resp) {
return;
}
toast("Screenshot has been saved to Trilium.", resp.noteId);
}
async function saveWholeScreenshot(pageUrl) {
const src = await takeWholeScreenshot();
const payload = await getImagePayloadFromSrc(src, pageUrl);
const resp = await triliumServerFacade.callService("POST", "clippings", payload);
if (!resp) {
return;
}
toast("Screenshot has been saved to Trilium.", resp.noteId);
}
async function saveImage(srcUrl, pageUrl) {
const payload = await getImagePayloadFromSrc(srcUrl, pageUrl);
const resp = await triliumServerFacade.callService("POST", "clippings", payload);
if (!resp) {
return;
}
toast("Image has been saved to Trilium.", resp.noteId);
}
async function saveWholePage() {
const payload = await sendMessageToActiveTab({name: 'trilium-save-page'});
await postProcessImages(payload);
const resp = await triliumServerFacade.callService('POST', 'notes', payload);
if (!resp) {
return;
}
toast("Page has been saved to Trilium.", resp.noteId);
}
async function saveLinkWithNote(title, content) {
const activeTab = await getActiveTab();
if (!title.trim()) {
title = activeTab.title;
}
const resp = await triliumServerFacade.callService('POST', 'notes', {
title: title,
content: content,
clipType: 'note',
pageUrl: activeTab.url
});
if (!resp) {
return false;
}
toast("Link with note has been saved to Trilium.", resp.noteId);
return true;
}
async function getTabsPayload(tabs) {
let content = '<ul>';
tabs.forEach(tab => {
content += `<li><a href="${tab.url}">${tab.title}</a></li>`
});
content += '</ul>';
const domainsCount = tabs.map(tab => tab.url)
.reduce((acc, url) => {
const hostname = new URL(url).hostname
return acc.set(hostname, (acc.get(hostname) || 0) + 1)
}, new Map());
let topDomains = [...domainsCount]
.sort((a, b) => {return b[1]-a[1]})
.slice(0,3)
.map(domain=>domain[0])
.join(', ')
if (tabs.length > 3) { topDomains += '...' }
return {
title: `${tabs.length} browser tabs: ${topDomains}`,
content: content,
clipType: 'tabs'
};
}
async function saveTabs() {
const tabs = await getWindowTabs();
const payload = await getTabsPayload(tabs);
const resp = await triliumServerFacade.callService('POST', 'notes', payload);
if (!resp) {
return;
}
const tabIds = tabs.map(tab=>{return tab.id});
toast(`${tabs.length} links have been saved to Trilium.`, resp.noteId, tabIds);
}
browser.contextMenus.onClicked.addListener(async function(info, tab) {
if (info.menuItemId === 'trilium-save-selection') {
await saveSelection();
}
else if (info.menuItemId === 'trilium-save-cropped-screenshot') {
await saveCroppedScreenshot(info.pageUrl);
}
else if (info.menuItemId === 'trilium-save-whole-screenshot') {
await saveWholeScreenshot(info.pageUrl);
}
else if (info.menuItemId === 'trilium-save-image') {
await saveImage(info.srcUrl, info.pageUrl);
}
else if (info.menuItemId === 'trilium-save-link') {
const link = document.createElement("a");
link.href = info.linkUrl;
// linkText might be available only in firefox
link.appendChild(document.createTextNode(info.linkText || info.linkUrl));
const activeTab = await getActiveTab();
const resp = await triliumServerFacade.callService('POST', 'clippings', {
title: activeTab.title,
content: link.outerHTML,
pageUrl: info.pageUrl
});
if (!resp) {
return;
}
toast("Link has been saved to Trilium.", resp.noteId);
}
else if (info.menuItemId === 'trilium-save-page') {
await saveWholePage();
}
else {
console.log("Unrecognized menuItemId", info.menuItemId);
}
});
browser.runtime.onMessage.addListener(async request => {
console.log("Received", request);
if (request.name === 'openNoteInTrilium') {
const resp = await triliumServerFacade.callService('POST', 'open/' + request.noteId);
if (!resp) {
return;
}
// desktop app is not available so we need to open in browser
if (resp.result === 'open-in-browser') {
const {triliumServerUrl} = await browser.storage.sync.get("triliumServerUrl");
if (triliumServerUrl) {
const noteUrl = triliumServerUrl + '/#' + request.noteId;
console.log("Opening new tab in browser", noteUrl);
browser.tabs.create({
url: noteUrl
});
}
else {
console.error("triliumServerUrl not found in local storage.");
}
}
}
else if (request.name === 'closeTabs') {
return await browser.tabs.remove(request.tabIds)
}
else if (request.name === 'load-script') {
return await browser.tabs.executeScript({file: request.file});
}
else if (request.name === 'save-cropped-screenshot') {
const activeTab = await getActiveTab();
return await saveCroppedScreenshot(activeTab.url);
}
else if (request.name === 'save-whole-screenshot') {
const activeTab = await getActiveTab();
return await saveWholeScreenshot(activeTab.url);
}
else if (request.name === 'save-whole-page') {
return await saveWholePage();
}
else if (request.name === 'save-link-with-note') {
return await saveLinkWithNote(request.title, request.content);
}
else if (request.name === 'save-tabs') {
return await saveTabs();
}
else if (request.name === 'trigger-trilium-search') {
triliumServerFacade.triggerSearchForTrilium();
}
else if (request.name === 'send-trilium-search-status') {
triliumServerFacade.sendTriliumSearchStatusToPopup();
}
else if (request.name === 'trigger-trilium-search-note-url') {
const activeTab = await getActiveTab();
triliumServerFacade.triggerSearchNoteByUrl(activeTab.url);
}
});

View File

@ -1,78 +0,0 @@
#!/bin/bash
# Trilium Web Clipper - Manifest V3 Verification Script
echo "🔍 Trilium Web Clipper Manifest V3 Conversion Verification"
echo "=========================================================="
# Check manifest.json structure
echo ""
echo "📋 Checking manifest.json..."
if grep -q '"manifest_version": 3' manifest.json; then
echo "✅ Manifest version 3 detected"
else
echo "❌ Manifest version 3 not found"
fi
if grep -q '"service_worker"' manifest.json; then
echo "✅ Service worker configuration found"
else
echo "❌ Service worker configuration missing"
fi
if grep -q '"scripting"' manifest.json; then
echo "✅ Scripting permission found"
else
echo "❌ Scripting permission missing"
fi
# Check file existence
echo ""
echo "📁 Checking required files..."
files=("background.js" "content.js" "utils.js" "trilium_server_facade.js" "popup/popup.js" "options/options.js")
for file in "${files[@]}"; do
if [ -f "$file" ]; then
echo "$file exists"
else
echo "$file missing"
fi
done
# Check for chrome API usage
echo ""
echo "🌐 Checking Chrome API usage..."
if grep -q "chrome\." background.js; then
echo "✅ Chrome APIs found in background.js"
else
echo "❌ Chrome APIs missing in background.js"
fi
if grep -q "chrome\." content.js; then
echo "✅ Chrome APIs found in content.js"
else
echo "❌ Chrome APIs missing in content.js"
fi
# Check ES module exports
echo ""
echo "📦 Checking ES module structure..."
if grep -q "export" utils.js; then
echo "✅ ES module exports found in utils.js"
else
echo "❌ ES module exports missing in utils.js"
fi
if grep -q "import" background.js; then
echo "✅ ES module imports found in background.js"
else
echo "❌ ES module imports missing in background.js"
fi
echo ""
echo "🚀 Verification complete!"
echo ""
echo "Next steps:"
echo "1. Open Chrome and go to chrome://extensions/"
echo "2. Enable Developer mode"
echo "3. Click 'Load unpacked' and select this directory"
echo "4. Test the extension functionality"