diff --git a/README.md b/README.md index 9edc674..f32d583 100644 --- a/README.md +++ b/README.md @@ -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 ]`. 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 diff --git a/TODO.org b/TODO.org index d7f4792..0d68b80 100644 --- a/TODO.org +++ b/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 diff --git a/package.json b/package.json index 66bb785..ab23993 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/js/background.js b/src/js/background.js index 3323ded..815b4e9 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -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?? + diff --git a/src/js/options.js b/src/js/options.js index 6872b90..4615f39 100644 --- a/src/js/options.js +++ b/src/js/options.js @@ -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}`; } diff --git a/src/js/options_page.js b/src/js/options_page.js new file mode 100644 index 0000000..bbadf8a --- /dev/null +++ b/src/js/options_page.js @@ -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); diff --git a/src/js/popup.js b/src/js/popup.js index 0b5e600..417b309 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -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 diff --git a/src/manifest.json b/src/manifest.json index 9b26e04..2f268ab 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -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", diff --git a/src/options.html b/src/options.html index 384a69f..a713a8e 100644 --- a/src/options.html +++ b/src/options.html @@ -5,5 +5,12 @@ + +
+ +
+ +
+
diff --git a/src/popup.html b/src/popup.html index 411af8a..67926e8 100644 --- a/src/popup.html +++ b/src/popup.html @@ -7,7 +7,7 @@
-
+
diff --git a/webpack.config.js b/webpack.config.js index c9f339c..fbdf57e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -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: {