Merge branch 'command-line'

jh-changes
Shin'ya Ueoka 7 years ago
commit 13fb726332
  1. 5
      manifest.json
  2. 1154
      package-lock.json
  3. 2
      package.json
  4. 2
      src/background/commands.js
  5. 62
      src/background/index.js
  6. 5
      src/background/key-queue.js
  7. 22
      src/background/tabs.js
  8. 46
      src/content/footer-line.css
  9. 78
      src/content/footer-line.js
  10. 33
      src/content/index.js
  11. 8
      src/shared/actions.js
  12. 6
      webpack.config.js

@ -11,5 +11,8 @@
], ],
"background": { "background": {
"scripts": ["build/background.js"] "scripts": ["build/background.js"]
} },
"permissions": [
"sessions"
]
} }

1154
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -22,6 +22,7 @@
"babel-loader": "^7.1.1", "babel-loader": "^7.1.1",
"babel-preset-es2015": "^6.24.1", "babel-preset-es2015": "^6.24.1",
"chai": "^4.1.1", "chai": "^4.1.1",
"css-loader": "^0.28.4",
"eslint": "^4.4.1", "eslint": "^4.4.1",
"karma": "^1.7.0", "karma": "^1.7.0",
"karma-firefox-launcher": "^1.0.1", "karma-firefox-launcher": "^1.0.1",
@ -30,6 +31,7 @@
"karma-sourcemap-loader": "^0.3.7", "karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^2.0.4", "karma-webpack": "^2.0.4",
"mocha": "^3.5.0", "mocha": "^3.5.0",
"style-loader": "^0.18.2",
"webpack": "^3.5.3" "webpack": "^3.5.3"
} }
} }

@ -0,0 +1,2 @@
export const OPEN = 'open';
export const TABOPEN = 'tabopen';

@ -1,21 +1,37 @@
import * as actions from '../shared/actions'; import * as actions from '../shared/actions';
import * as tabs from './tabs'; import * as tabs from './tabs';
import * as commands from './commands';
import KeyQueue from './key-queue'; import KeyQueue from './key-queue';
const queue = new KeyQueue(); const queue = new KeyQueue();
const keyDownHandle = (request) => { const keyDownHandle = (request, sender, sendResponse) => {
return queue.push({ let action = queue.push({
code: request.code, code: request.code,
shift: request.shift, shift: request.shift,
ctrl: request.ctrl, ctrl: request.ctrl,
alt: request.alt, alt: request.alt,
meta: request.meta meta: request.meta
}) });
} if (!action) {
return;
}
if (actions.isBackgroundAction(action[0])) {
doBackgroundAction(sender, action);
} else if (actions.isContentAction(action[0])) {
sendResponse(action);
}
};
const doBackgroundAction = (sender, action) => { const doBackgroundAction = (sender, action) => {
switch(action[0]) { switch(action[0]) {
case actions.TABS_CLOSE:
tabs.closeTab(sender.tab.id);
break;
case actions.TABS_REOPEN:
tabs.reopenTab();
break;
case actions.TABS_PREV: case actions.TABS_PREV:
tabs.selectPrevTab(sender.tab.index, actions[1] || 1); tabs.selectPrevTab(sender.tab.index, actions[1] || 1);
break; break;
@ -25,22 +41,36 @@ const doBackgroundAction = (sender, action) => {
} }
} }
browser.runtime.onMessage.addListener((request, sender, sendResponse) => { const normalizeUrl = (string) => {
let action = null; try {
return new URL(string).href
switch (request.type) { } catch (e) {
case 'event.keydown': return 'http://' + string;
action = keyDownHandle(request);
break;
} }
}
if (action == null) { const cmdEnterHandle = (request, sender) => {
let words = request.text.split(' ').filter((s) => s.length > 0);
switch (words[0]) {
case commands.OPEN:
browser.tabs.update(sender.tab.id, { url: normalizeUrl(words[1]) });
return;
case commands.TABOPEN:
browser.tabs.create({ url: normalizeUrl(words[1]) });
return; return;
} }
};
if (actions.isBackgroundAction(action[0])) { browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
doBackgroundAction(sender, action); switch (request.type) {
} else if (actions.isContentAction(action[0])) { case 'event.keydown':
sendResponse(action); keyDownHandle(request, sender, sendResponse);
break;
case 'event.cmd.enter':
cmdEnterHandle(request, sender, sendResponse);
break;
case 'event.cmd.suggest':
// TODO make suggestion and return via sendResponse
break;
} }
}); });

@ -2,10 +2,15 @@ import * as keys from './keys';
import * as actions from '../shared/actions'; import * as actions from '../shared/actions';
const DEFAULT_KEYMAP = [ const DEFAULT_KEYMAP = [
{ keys: [{ code: KeyboardEvent.DOM_VK_SEMICOLON, shift: true }], action: [ actions.CMD_OPEN ]},
{ keys: [{ code: KeyboardEvent.DOM_VK_O }], action: [ actions.CMD_TABS_OPEN, false ]},
{ keys: [{ code: KeyboardEvent.DOM_VK_O, shift: true }], action: [ actions.CMD_TABS_OPEN, true ]},
{ keys: [{ code: KeyboardEvent.DOM_VK_K }], action: [ actions.SCROLL_UP, 1 ]}, { keys: [{ code: KeyboardEvent.DOM_VK_K }], action: [ actions.SCROLL_UP, 1 ]},
{ keys: [{ code: KeyboardEvent.DOM_VK_J }], action: [ actions.SCROLL_DOWN, 1 ]}, { keys: [{ code: KeyboardEvent.DOM_VK_J }], action: [ actions.SCROLL_DOWN, 1 ]},
{ keys: [{ code: KeyboardEvent.DOM_VK_G }, { code: KeyboardEvent.DOM_VK_G }], action: [ actions.SCROLL_TOP ]}, { keys: [{ code: KeyboardEvent.DOM_VK_G }, { code: KeyboardEvent.DOM_VK_G }], action: [ actions.SCROLL_TOP ]},
{ keys: [{ code: KeyboardEvent.DOM_VK_G, shift: true }], action: [ actions.SCROLL_BOTTOM ]}, { keys: [{ code: KeyboardEvent.DOM_VK_G, shift: true }], action: [ actions.SCROLL_BOTTOM ]},
{ keys: [{ code: KeyboardEvent.DOM_VK_D }], action: [ actions.TABS_CLOSE ]},
{ keys: [{ code: KeyboardEvent.DOM_VK_U }], action: [ actions.TABS_REOPEN]},
{ keys: [{ code: KeyboardEvent.DOM_VK_H }], action: [ actions.TABS_PREV, 1 ]}, { keys: [{ code: KeyboardEvent.DOM_VK_H }], action: [ actions.TABS_PREV, 1 ]},
{ keys: [{ code: KeyboardEvent.DOM_VK_L }], action: [ actions.TABS_NEXT, 1 ]}, { keys: [{ code: KeyboardEvent.DOM_VK_L }], action: [ actions.TABS_NEXT, 1 ]},
] ]

@ -1,3 +1,23 @@
const closeTab = (id) => {
browser.tabs.remove(id);
};
const reopenTab = () => {
browser.sessions.getRecentlyClosed({
maxResults: 1
}).then((sessions) => {
if (sessions.length === 0) {
return;
}
let session = sessions[0];
if (session.tab) {
browser.sessions.restore(session.tab.sessionId);
} else {
browser.sessions.restore(session.window.sessionId);
}
});
};
const selectPrevTab = (current, count) => { const selectPrevTab = (current, count) => {
chrome.tabs.query({ currentWindow: true }, (tabs) => { chrome.tabs.query({ currentWindow: true }, (tabs) => {
if (tabs.length < 2) { if (tabs.length < 2) {
@ -20,4 +40,4 @@ const selectNextTab = (current, count) => {
}); });
}; };
export { selectNextTab, selectPrevTab }; export { closeTab, reopenTab, selectNextTab, selectPrevTab };

@ -0,0 +1,46 @@
.vimvixen-footerline {
border-top: 1px solid gray;
bottom: 0;
box-sizing: border-box;
font-family: monospace;
font-size: 12px;
left: 0;
margin: 0;
padding: 0;
position: fixed;
right: 0;
z-index: 10000;
}
.vimvixen-footerline-title {
background-color: lightgray;
font-weight: bold;
margin: 0;
padding: 0;
}
.vimvixen-footerline-container-outer {
background-color: white;
position: relative;
}
.vimvixen-footerline-container-outer:before {
content: ':';
background-color: white;
float: left;
text-align: right;
width: 12px;
}
.vimvixen-footerline-container-inner {
position: absolute;
left: 12px;
right: 0;
}
.vimvixen-footerline-input {
margin: 0;
padding: 0;
width: 100%;
border: none;
}

@ -0,0 +1,78 @@
import './footer-line.css';
export default class FooterLine {
constructor(doc, initial = '') {
this.initUi(doc);
this.enteredCallback = () => {}
this.promptChangeCallback = () => {}
this.input.addEventListener('blur', this.handleBlur.bind(this));
this.input.addEventListener('keydown', this.handleKeydown.bind(this));
this.input.addEventListener('keyup', this.handleKeyup.bind(this));
this.input.value = initial;
}
initUi(doc) {
this.title = doc.createElement('p');
this.title.className = 'vimvixen-footerline-title';
let containerInner = doc.createElement('div');
containerInner.className = 'vimvixen-footerline-container-inner';
let containerOuter = doc.createElement('div');
containerOuter.className = 'vimvixen-footerline-container-outer';
this.input = doc.createElement('input');
this.input.className = 'vimvixen-footerline-input';
this.wrapper = doc.createElement('div');
this.wrapper.className = 'vimvixen-footerline';
containerOuter.append(containerInner);
containerInner.append(this.input);
this.wrapper.append(this.title);
this.wrapper.append(containerOuter);
doc.body.append(this.wrapper)
}
focus() {
this.input.focus();
}
remove() {
this.wrapper.remove();
}
onPromptChange(callback) {
this.promptChangeCallback = callback;
}
onEntered(callback) {
this.enteredCallback = callback;
}
handleBlur() {
this.remove();
}
handleKeydown(e) {
this.prevValue = e.target.value;
switch(e.keyCode) {
case KeyboardEvent.DOM_VK_ESCAPE:
this.remove();
break;
case KeyboardEvent.DOM_VK_RETURN:
this.enteredCallback(e);
break;
}
}
handleKeyup(e) {
if (e.target.value === this.prevValue) {
return;
}
this.promptChangeCallback(e);
this.prevValue = e.target.value;
}
}

@ -1,12 +1,45 @@
import * as scrolls from './scrolls'; import * as scrolls from './scrolls';
import FooterLine from './footer-line';
import * as actions from '../shared/actions'; import * as actions from '../shared/actions';
var footer = null;
const createFooterLine = (initial = '') => {
footer = new FooterLine(document, initial);
footer.onPromptChange((e) => {
let request = {
type: 'event.cmd.suggest',
text: e.target.value
};
browser.runtime.sendMessage(request);
});
footer.onEntered((e) => {
let request = {
type: 'event.cmd.enter',
text: e.target.value
};
browser.runtime.sendMessage(request);
});
footer.focus();
}
const invokeEvent = (action) => { const invokeEvent = (action) => {
if (typeof action === 'undefined' || action === null) { if (typeof action === 'undefined' || action === null) {
return; return;
} }
switch (action[0]) { switch (action[0]) {
case actions.CMD_OPEN:
createFooterLine();
break;
case actions.CMD_TABS_OPEN:
if (action[1] || false) {
// alter url
createFooterLine('open ' + window.location.href);
} else {
createFooterLine('open ');
}
break;
case actions.SCROLL_UP: case actions.SCROLL_UP:
scrolls.scrollUp(window, action[1] || 1); scrolls.scrollUp(window, action[1] || 1);
break; break;

@ -1,3 +1,7 @@
export const CMD_OPEN = 'cmd.open';
export const CMD_TABS_OPEN = 'cmd.tabs.open';
export const TABS_CLOSE = 'tabs.close';
export const TABS_REOPEN = 'tabs.reopen';
export const TABS_PREV = 'tabs.prev'; export const TABS_PREV = 'tabs.prev';
export const TABS_NEXT = 'tabs.next'; export const TABS_NEXT = 'tabs.next';
export const SCROLL_UP = 'scroll.up'; export const SCROLL_UP = 'scroll.up';
@ -6,11 +10,15 @@ export const SCROLL_TOP = 'scroll.top';
export const SCROLL_BOTTOM = 'scroll.bottom'; export const SCROLL_BOTTOM = 'scroll.bottom';
const BACKGROUND_ACTION_SET = new Set([ const BACKGROUND_ACTION_SET = new Set([
TABS_CLOSE,
TABS_REOPEN,
TABS_PREV, TABS_PREV,
TABS_NEXT TABS_NEXT
]); ]);
const CONTENT_ACTION_SET = new Set([ const CONTENT_ACTION_SET = new Set([
CMD_OPEN,
CMD_TABS_OPEN,
SCROLL_UP, SCROLL_UP,
SCROLL_DOWN, SCROLL_DOWN,
SCROLL_TOP, SCROLL_TOP,

@ -23,7 +23,11 @@ module.exports = {
query: { query: {
presets: [ 'es2015' ] presets: [ 'es2015' ]
} }
} },
{
test: /\.css$/,
loader: 'style-loader!css-loader',
},
] ]
}, },