Settings page for port, notification and default tags
This commit is contained in:
parent
d0f7982c4f
commit
a658234cf3
|
@ -14,7 +14,7 @@ No third party dependencies! Just `python 3.6`.
|
|||
Or alternatively, just run it directly if you don't want to autostart it: `server/grasp_server.py --path /path/to/your/capture.org [--port <custom_port>]`.
|
||||
2. Install chrome extension and configure hotkeys
|
||||
|
||||
That's it! **Currently port `12212` is hardcoded** in the extension, but it will be configurable later.
|
||||
That's it! If you're using custom port make sure it's same as in the extension settings.
|
||||
|
||||
# Motivation
|
||||
Why use org-capture? Well, it's hard to explain, maybe some other time... However if you do know you want to use it instead/alongside your browser bookmarks, by default
|
||||
|
@ -45,6 +45,7 @@ You're gonna need `npm` for building extension.
|
|||
After that you can find the extension in `build` directory and 'Load unpacked' if necessary. There is also Flow and Eslint set up.
|
||||
|
||||
# Permissions used
|
||||
* `storage` for settings
|
||||
* `notifications` for showing error notification
|
||||
* `activeTab` for requesting url, title and selected text
|
||||
* `http://localhost/capture` for talking with backend
|
||||
|
|
19
TODO.org
19
TODO.org
|
@ -1,18 +1,12 @@
|
|||
* DONE [#B] release somewhere? not sure if necessary
|
||||
CLOSED: [2019-01-07 Mon 23:57]
|
||||
** TODO [#C] post on reddit?
|
||||
* DONE [#B] get rid of kython, run ci for python too?
|
||||
CLOSED: [2019-01-07 Mon 23:57]
|
||||
|
||||
* TODO [#B] doesn't look like hotkey working unless you manually rebind.. wtf?
|
||||
|
||||
* TODO [#C] update screens and video now that the notification is nicer
|
||||
|
||||
* TODO [#C] how to test it? try on different pages?
|
||||
* TODO [#C] some sort of delayed append logic? e.g. don't dump immediately and add as subitems?
|
||||
* TODO [#C] make notification configurable or only in case of errors
|
||||
* TODO [#C] or, add menu to extension?
|
||||
|
||||
* TODO [#C] custom capture template?
|
||||
* TODO [#D] do not minfy for dev
|
||||
* TODO [#D] make manifest templated so we don't have to hardcode actions
|
||||
|
||||
|
@ -36,9 +30,18 @@ Now, the content of `build` folder will be the extension ready to be submitted t
|
|||
* DONE [#A] entering additional information and tags
|
||||
CLOSED: [2019-01-06 Sun 20:41]
|
||||
|
||||
* DONE [#B] circleci?
|
||||
CLOSED: [2019-01-13 Sun 13:01]
|
||||
* DONE [#B] release somewhere? not sure if necessary
|
||||
CLOSED: [2019-01-07 Mon 23:57]
|
||||
** TODO [#C] post on reddit?
|
||||
* DONE [#B] get rid of kython, run ci for python too?
|
||||
CLOSED: [2019-01-07 Mon 23:57]
|
||||
|
||||
* DONE [#B] docs on using
|
||||
CLOSED: [2019-01-06 Sun 21:39]
|
||||
* TODO [#B] circleci?
|
||||
* DONE [#C] make notification configurable or only in case of errors
|
||||
CLOSED: [2019-01-13 Sun 13:01]
|
||||
* DONE [#C] motivation
|
||||
CLOSED: [2019-01-06 Sun 21:35]
|
||||
** DONE the benefit is that you can also add anything as a backend, e.g. you might be more of markdown fan or todo.txt
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "grasp-extension",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"description": "A reliable way of capturing web pages and content",
|
||||
"scripts": {
|
||||
"build": "webpack --progress --profile --colors",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// TODO do I really need to annotate all files with @flow??
|
||||
|
||||
import {COMMAND_CAPTURE_SIMPLE, METHOD_CAPTURE_WITH_EXTRAS, showNotification} from './common';
|
||||
import {defaultTagStr, capture_url} from './options';
|
||||
import {get_options, capture_url} from './options';
|
||||
|
||||
type Params = {
|
||||
url: string,
|
||||
|
@ -15,12 +15,18 @@ type Params = {
|
|||
|
||||
function makeCaptureRequest(
|
||||
params: Params,
|
||||
options,
|
||||
) {
|
||||
if (params.tag_str == null) {
|
||||
params.tag_str = options.default_tags;
|
||||
}
|
||||
|
||||
const data = JSON.stringify(params);
|
||||
console.log(`[background] capturing ${data}`);
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('POST', capture_url(), true);
|
||||
const curl = capture_url(options);
|
||||
request.open('POST', curl, true);
|
||||
request.onreadystatechange = () => {
|
||||
if (request.readyState != 4) {
|
||||
return;
|
||||
|
@ -36,7 +42,9 @@ function makeCaptureRequest(
|
|||
const response = JSON.parse(rtext);
|
||||
const path = response.path;
|
||||
console.log(`[background] success: ${response}`);
|
||||
showNotification(`OK: captured to ${path}`);
|
||||
if (options.notification) {
|
||||
showNotification(`OK: captured to ${path}`);
|
||||
}
|
||||
} catch (err) {
|
||||
had_error = true;
|
||||
error_message = error_message.concat(String(err));
|
||||
|
@ -45,7 +53,7 @@ function makeCaptureRequest(
|
|||
} else {
|
||||
had_error = true;
|
||||
if (status == 0) {
|
||||
error_message = error_message.concat(` ${capture_url()} must be unavailable `);
|
||||
error_message = error_message.concat(` ${curl} must be unavailable `);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,25 +78,23 @@ function capture(comment: ?string = null, tag_str: ?string = null) {
|
|||
showNotification('ERROR: trying to capture null');
|
||||
return;
|
||||
}
|
||||
if (tag_str === null) {
|
||||
tag_str = defaultTagStr();
|
||||
}
|
||||
|
||||
const url: string = tab.url;
|
||||
const title: ?string = tab.title;
|
||||
|
||||
// console.log('action!');
|
||||
// ugh.. https://stackoverflow.com/a/19165930/706389
|
||||
chrome.tabs.executeScript( {
|
||||
code: "window.getSelection().toString();"
|
||||
}, selections => {
|
||||
const selection = selections == null ? null : selections[0];
|
||||
makeCaptureRequest({
|
||||
url: url,
|
||||
title: title,
|
||||
selection: selection,
|
||||
comment: comment,
|
||||
tag_str: tag_str,
|
||||
get_options(opts => {
|
||||
// console.log('action!');
|
||||
// ugh.. https://stackoverflow.com/a/19165930/706389
|
||||
chrome.tabs.executeScript( {
|
||||
code: "window.getSelection().toString();"
|
||||
}, selections => {
|
||||
const selection = selections == null ? null : selections[0];
|
||||
makeCaptureRequest({
|
||||
url: url,
|
||||
title: title,
|
||||
selection: selection,
|
||||
comment: comment,
|
||||
tag_str: tag_str,
|
||||
}, opts);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -110,3 +116,4 @@ chrome.runtime.onMessage.addListener((message: any, sender: chrome$MessageSender
|
|||
});
|
||||
|
||||
// TODO handle cannot access chrome:// url??
|
||||
|
||||
|
|
|
@ -2,15 +2,32 @@
|
|||
|
||||
const capture_endpoint = 'capture';
|
||||
|
||||
function port(): string {
|
||||
return "12212";
|
||||
type Options = {
|
||||
port: string;
|
||||
default_tags: string;
|
||||
notification: boolean;
|
||||
}
|
||||
|
||||
// none means do not use tags
|
||||
export function defaultTagStr (): ?string {
|
||||
return 'grasp';
|
||||
function default_options(): Options {
|
||||
return {
|
||||
port: "12212",
|
||||
default_tags: "grasp",
|
||||
notification: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function capture_url (): string {
|
||||
return `http://localhost:${port()}/${capture_endpoint}`;
|
||||
export function get_options(cb: (Options) => void) {
|
||||
chrome.storage.local.get(null, res => {
|
||||
res = {...default_options(), ...res};
|
||||
cb(res);
|
||||
});
|
||||
}
|
||||
|
||||
export function set_options(opts: Options, cb: () => void) {
|
||||
console.log('Saving %s', JSON.stringify(opts));
|
||||
chrome.storage.local.set(opts, cb);
|
||||
}
|
||||
|
||||
export function capture_url(options: Options): string {
|
||||
return `http://localhost:${options.port}/${capture_endpoint}`;
|
||||
}
|
||||
|
|
46
src/js/options_page.js
Normal file
46
src/js/options_page.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
/* @flow */
|
||||
import {get_options, set_options} from './options';
|
||||
|
||||
const SAVE_ID = 'save_id';
|
||||
const PORT_ID = 'port_id';
|
||||
const NOTIFICATION_ID = 'notification_id';
|
||||
const DEFAULT_TAGS_ID = 'default_tags_id';
|
||||
// TODO specify capture path here?
|
||||
|
||||
function getPort(): HTMLInputElement {
|
||||
return ((document.getElementById(PORT_ID): any): HTMLInputElement);
|
||||
}
|
||||
|
||||
function getDefaultTags(): HTMLInputElement {
|
||||
return ((document.getElementById(DEFAULT_TAGS_ID): any): HTMLInputElement);
|
||||
// TODO if empty, return null?
|
||||
}
|
||||
|
||||
function getEnableNotification(): HTMLInputElement {
|
||||
return ((document.getElementById(NOTIFICATION_ID): any): HTMLInputElement);
|
||||
}
|
||||
|
||||
function getSaveButton(): HTMLInputElement {
|
||||
return ((document.getElementById(SAVE_ID): any): HTMLInputElement);
|
||||
}
|
||||
|
||||
function restoreOptions() {
|
||||
get_options(opts => {
|
||||
getPort().value = opts.port;
|
||||
getDefaultTags().value = opts.default_tags;
|
||||
getEnableNotification().checked = opts.notification;
|
||||
});
|
||||
}
|
||||
|
||||
function saveOptions() {
|
||||
const opts = {
|
||||
port: getPort().value, // TODO validate??
|
||||
default_tags: getDefaultTags().value,
|
||||
notification: getEnableNotification().checked,
|
||||
};
|
||||
set_options(opts, () => { console.log('fewf'); alert('Saved!'); });
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', restoreOptions);
|
||||
getSaveButton().addEventListener('click', saveOptions);
|
|
@ -1,6 +1,7 @@
|
|||
/* @flow */
|
||||
|
||||
import {METHOD_CAPTURE_WITH_EXTRAS} from './common';
|
||||
import {get_options} from './options';
|
||||
|
||||
// TODO template it in html too?
|
||||
const BUTTON_ID = 'button_id';
|
||||
|
@ -63,6 +64,7 @@ function setupPage () {
|
|||
comment.addEventListener('keydown', ctrlEnterSubmit);
|
||||
|
||||
const tags = getTags();
|
||||
get_options(opts => {tags.value = opts.default_tags;});
|
||||
tags.addEventListener('keydown', ctrlEnterSubmit);
|
||||
tags.addEventListener('focus', () => moveCaretToEnd(tags)); // to put cursor to the end of tags when tabbed
|
||||
|
||||
|
|
|
@ -4,7 +4,11 @@
|
|||
"icons": {
|
||||
"128": "unicorn.png"
|
||||
},
|
||||
"options_page": "options.html",
|
||||
"options_ui": {
|
||||
"page": "options.html",
|
||||
"chrome_style": true
|
||||
},
|
||||
|
||||
"background": {
|
||||
"page": "background.html"
|
||||
},
|
||||
|
@ -13,6 +17,7 @@
|
|||
"default_title": "Capture page, with extra information"
|
||||
},
|
||||
"permissions": [
|
||||
"storage",
|
||||
"notifications",
|
||||
"activeTab",
|
||||
"http://localhost/capture",
|
||||
|
|
|
@ -5,5 +5,12 @@
|
|||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<label for="port_id">Port</label>
|
||||
<div><input type="text" id="port_id"/></div>
|
||||
<label for="notification_id">Notification after capturing</label>
|
||||
<div><input type="checkbox" id="notification_id"/></div>
|
||||
<label for="default_tags_id">Default tags</label>
|
||||
<div><input type="text" id="default_tags_id" /></div>
|
||||
<div><button id="save_id">Save</button></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<!-- TODO ugh didn't work -->
|
||||
<!-- <%= htmlWebpackPlugin.options.ids.tags %> -->
|
||||
<div><textarea id="comment_id" rows="4" cols="50" placeholder="Enter your comments; press Ctrl-Enter to capture"></textarea></div>
|
||||
<div><textarea id="tags_id" rows="1" cols="50">grasp</textarea></div>
|
||||
<div><textarea id="tags_id" rows="1" cols="50"></textarea></div>
|
||||
<div><button id="button_id" type="button" align="right">capture!</button></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -26,7 +26,7 @@ var options = {
|
|||
mode: 'development',
|
||||
entry: {
|
||||
popup: path.join(__dirname, "src", "js", "popup.js"),
|
||||
options: path.join(__dirname, "src", "js", "options.js"),
|
||||
options: path.join(__dirname, "src", "js", "options_page.js"),
|
||||
background: path.join(__dirname, "src", "js", "background.js")
|
||||
},
|
||||
output: {
|
||||
|
|
Loading…
Reference in a new issue