mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Clipper: Add ability to launch clipper window with shortcut (#2272)
* Clipper: Add ability to launch clipper window with shortcut This change adds a command to the manifest.json file for the web clipper which launches the webclipper rather than clicking on it. Because this is a WebExtensions feature and not something homegrown, users are able to change (or remove) the shortcut using native browser functionality. * Add commands for all clipping options * Remove empty suggestedKeys property from extension manifest * Add ability to focus the webclipper buttons * Remove debug log * Change sendClipMessage warning to error * Refactor to add a sendContentToJoplin command * Update index.js Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
This commit is contained in:
parent
d278809659
commit
4bd326f72c
@ -76,3 +76,59 @@ browser_.runtime.onMessage.addListener(async (command) => {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
async function getActiveTabs() {
|
||||
const options = { active: true, currentWindow: true };
|
||||
if (browserSupportsPromises_) return browser_.tabs.query(options);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
browser_.tabs.query(options, (tabs) => {
|
||||
resolve(tabs);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function sendClipMessage(clipType) {
|
||||
const tabs = await getActiveTabs();
|
||||
if (!tabs || !tabs.length) {
|
||||
console.error('No active tabs');
|
||||
return;
|
||||
}
|
||||
const tabId = tabs[0].id;
|
||||
// send a message to the content script on the active tab (assuming it's there)
|
||||
const message = {
|
||||
shouldSendToJoplin: true,
|
||||
};
|
||||
switch (clipType) {
|
||||
case 'clipCompletePage':
|
||||
message.name = 'completePageHtml';
|
||||
message.preProcessFor = 'markdown';
|
||||
break;
|
||||
case 'clipCompletePageHtml':
|
||||
message.name = 'completePageHtml';
|
||||
message.preProcessFor = 'html';
|
||||
break;
|
||||
case 'clipSimplifiedPage':
|
||||
message.name = 'simplifiedPageHtml';
|
||||
break;
|
||||
case 'clipUrl':
|
||||
message.name = 'pageUrl';
|
||||
break;
|
||||
case 'clipSelection':
|
||||
message.name = 'selectedHtml';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (message.name) {
|
||||
browser_.tabs.sendMessage(tabId, message);
|
||||
}
|
||||
}
|
||||
|
||||
browser_.commands.onCommand.addListener(function(command) {
|
||||
// We could enumerate these twice, but since we're in here first,
|
||||
// why not save ourselves the trouble with this convention
|
||||
if (command.startsWith('clip')) {
|
||||
sendClipMessage(command);
|
||||
}
|
||||
});
|
||||
|
@ -266,12 +266,13 @@
|
||||
|
||||
async function prepareCommandResponse(command) {
|
||||
console.info(`Got command: ${command.name}`);
|
||||
const shouldSendToJoplin = !!command.shouldSendToJoplin;
|
||||
|
||||
const convertToMarkup = command.preProcessFor ? command.preProcessFor : 'markdown';
|
||||
|
||||
const clippedContentResponse = (title, html, imageSizes, anchorNames, stylesheets) => {
|
||||
return {
|
||||
name: 'clippedContent',
|
||||
name: shouldSendToJoplin ? 'sendContentToJoplin' : 'clippedContent',
|
||||
title: title,
|
||||
html: html,
|
||||
base_url: baseUrl(),
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Joplin Web Clipper [DEV]",
|
||||
"version": "1.0.19",
|
||||
"version": "1.0.20",
|
||||
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
|
||||
"homepage_url": "https://joplinapp.org",
|
||||
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
|
||||
@ -33,6 +33,31 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"commands": {
|
||||
"_execute_browser_action": {
|
||||
"suggested_key": {
|
||||
"default": "Alt+Shift+J"
|
||||
}
|
||||
},
|
||||
"clipCompletePage": {
|
||||
"suggested_key": {
|
||||
"default": "Alt+Shift+C"
|
||||
},
|
||||
"description": "Clip complete page (uses last selected notebook)"
|
||||
},
|
||||
"clipCompletePageHtml": {
|
||||
"description": "Clip complete page (HTML) (uses last selected notebook)"
|
||||
},
|
||||
"clipSimplifiedPage": {
|
||||
"description": "Clip simplified page (uses last selected notebook)"
|
||||
},
|
||||
"clipUrl": {
|
||||
"description": "Clip url (uses last selected notebook)"
|
||||
},
|
||||
"clipSelection": {
|
||||
"description": "Clip selection (uses last selected notebook)"
|
||||
}
|
||||
},
|
||||
"background": {
|
||||
"scripts": [
|
||||
"background.js"
|
||||
|
@ -52,9 +52,11 @@
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
min-height: 31px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.App a.Button:hover {
|
||||
.App a.Button:hover,
|
||||
.App a.Button:focus {
|
||||
background-color: #1E89E6;
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ class PreviewComponent extends React.PureComponent {
|
||||
<h2>Title:</h2>
|
||||
<input className={'Title'} value={this.props.title} onChange={this.props.onTitleChange}/>
|
||||
<p><span>Type:</span> {commandUserString(this.props.command)}</p>
|
||||
<a className={'Confirm Button'} onClick={this.props.onConfirmClick}>Confirm</a>
|
||||
<a className={'Confirm Button'} href="#" onClick={this.props.onConfirmClick}>Confirm</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -384,12 +384,12 @@ class AppComponent extends Component {
|
||||
<div className="App">
|
||||
<div className="Controls">
|
||||
<ul>
|
||||
<li><a className="Button" onClick={this.clipSimplified_click} title={simplifiedPageButtonTooltip}>{simplifiedPageButtonLabel}</a></li>
|
||||
<li><a className="Button" onClick={this.clipComplete_click}>Clip complete page</a></li>
|
||||
<li><a className="Button" onClick={this.clipCompleteHtml_click}>Clip complete page (HTML) (Beta)</a></li>
|
||||
<li><a className="Button" onClick={this.clipSelection_click}>Clip selection</a></li>
|
||||
<li><a className="Button" onClick={this.clipScreenshot_click}>Clip screenshot</a></li>
|
||||
<li><a className="Button" onClick={this.clipUrl_click}>Clip URL</a></li>
|
||||
<li><a className="Button" href="#" onClick={this.clipSimplified_click} title={simplifiedPageButtonTooltip}>{simplifiedPageButtonLabel}</a></li>
|
||||
<li><a className="Button" href="#" onClick={this.clipComplete_click}>Clip complete page</a></li>
|
||||
<li><a className="Button" href="#" onClick={this.clipCompleteHtml_click}>Clip complete page (HTML) (Beta)</a></li>
|
||||
<li><a className="Button" href="#" onClick={this.clipSelection_click}>Clip selection</a></li>
|
||||
<li><a className="Button" href="#" onClick={this.clipScreenshot_click}>Clip screenshot</a></li>
|
||||
<li><a className="Button" href="#" onClick={this.clipUrl_click}>Clip URL</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{ foldersComp() }
|
||||
|
@ -6,15 +6,32 @@ class Bridge {
|
||||
this.nounce_ = Date.now();
|
||||
}
|
||||
|
||||
async init(browser, browserSupportsPromises, dispatch) {
|
||||
async init(browser, browserSupportsPromises, store) {
|
||||
console.info('Popup: Init bridge');
|
||||
|
||||
this.browser_ = browser;
|
||||
this.dispatch_ = dispatch;
|
||||
this.dispatch_ = store.dispatch;
|
||||
this.store_ = store;
|
||||
this.browserSupportsPromises_ = browserSupportsPromises;
|
||||
this.clipperServerPort_ = null;
|
||||
this.clipperServerPortStatus_ = 'searching';
|
||||
|
||||
function convertCommandToContent(command) {
|
||||
return {
|
||||
title: command.title,
|
||||
body_html: command.html,
|
||||
base_url: command.base_url,
|
||||
source_url: command.url,
|
||||
parent_id: command.parent_id,
|
||||
tags: command.tags || '',
|
||||
image_sizes: command.image_sizes || {},
|
||||
anchor_names: command.anchor_names || [],
|
||||
source_command: command.source_command,
|
||||
convert_to: command.convert_to,
|
||||
stylesheets: command.stylesheets,
|
||||
};
|
||||
}
|
||||
|
||||
this.browser_notify = async (command) => {
|
||||
console.info('Popup: Got command:', command);
|
||||
|
||||
@ -26,30 +43,26 @@ class Bridge {
|
||||
}
|
||||
|
||||
if (command.name === 'clippedContent') {
|
||||
const content = {
|
||||
title: command.title,
|
||||
body_html: command.html,
|
||||
base_url: command.base_url,
|
||||
source_url: command.url,
|
||||
parent_id: command.parent_id,
|
||||
tags: command.tags || '',
|
||||
image_sizes: command.image_sizes || {},
|
||||
anchor_names: command.anchor_names || [],
|
||||
source_command: command.source_command,
|
||||
convert_to: command.convert_to,
|
||||
stylesheets: command.stylesheets,
|
||||
};
|
||||
|
||||
const content = convertCommandToContent(command);
|
||||
this.dispatch({ type: 'CLIPPED_CONTENT_SET', content: content });
|
||||
}
|
||||
|
||||
if (command.name === 'sendContentToJoplin') {
|
||||
const content = convertCommandToContent(command);
|
||||
this.dispatch({ type: 'CLIPPED_CONTENT_SET', content: content });
|
||||
|
||||
const state = this.store_.getState();
|
||||
content.parent_id = state.selectedFolderId;
|
||||
if (content.parent_id) {
|
||||
this.sendContentToJoplin(content);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.name === 'isProbablyReaderable') {
|
||||
this.dispatch({ type: 'IS_PROBABLY_READERABLE', value: command.value });
|
||||
}
|
||||
};
|
||||
|
||||
this.browser_.runtime.onMessage.addListener(this.browser_notify);
|
||||
|
||||
const backgroundPage = await this.backgroundPage(this.browser_);
|
||||
|
||||
// Not sure why the getBackgroundPage() sometimes returns null, so
|
||||
@ -354,7 +367,6 @@ class Bridge {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const bridge_ = new Bridge();
|
||||
|
@ -105,7 +105,7 @@ async function main() {
|
||||
|
||||
console.info('Popup: Init bridge and restore state...');
|
||||
|
||||
await bridge().init(window.browser ? window.browser : window.chrome, !!window.browser, store.dispatch);
|
||||
await bridge().init(window.browser ? window.browser : window.chrome, !!window.browser, store);
|
||||
|
||||
console.info('Popup: Creating React app...');
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user