Merge branch 'configurable-keymap'
This commit is contained in:
commit
1145eb3478
15 changed files with 167 additions and 71 deletions
|
@ -15,11 +15,15 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"permissions": [
|
"permissions": [
|
||||||
|
"history",
|
||||||
"sessions",
|
"sessions",
|
||||||
"tabs",
|
"storage",
|
||||||
"history"
|
"tabs"
|
||||||
],
|
],
|
||||||
"web_accessible_resources": [
|
"web_accessible_resources": [
|
||||||
"build/console.html"
|
"build/console.html"
|
||||||
]
|
],
|
||||||
|
"options_ui": {
|
||||||
|
"page": "build/settings.html"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,4 +8,5 @@ export default {
|
||||||
// User input
|
// User input
|
||||||
INPUT_KEY_PRESS: 'input.key,press',
|
INPUT_KEY_PRESS: 'input.key,press',
|
||||||
INPUT_CLEAR_KEYS: 'input.clear.keys',
|
INPUT_CLEAR_KEYS: 'input.clear.keys',
|
||||||
|
INPUT_SET_KEYMAPS: 'input.set,keymaps',
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,4 +14,11 @@ const clearKeys = () => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export { keyPress, clearKeys };
|
const setKeymaps = (keymaps) => {
|
||||||
|
return {
|
||||||
|
type: actions.INPUT_SET_KEYMAPS,
|
||||||
|
keymaps: keymaps
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export { keyPress, clearKeys, setKeymaps };
|
||||||
|
|
|
@ -6,15 +6,15 @@ import * as zooms from '../background/zooms';
|
||||||
|
|
||||||
const exec = (operation, tab) => {
|
const exec = (operation, tab) => {
|
||||||
switch (operation.type) {
|
switch (operation.type) {
|
||||||
case operations.TABS_CLOSE:
|
case operations.TAB_CLOSE:
|
||||||
return tabs.closeTab(tab.id);
|
return tabs.closeTab(tab.id);
|
||||||
case operations.TABS_REOPEN:
|
case operations.TAB_REOPEN:
|
||||||
return tabs.reopenTab();
|
return tabs.reopenTab();
|
||||||
case operations.TABS_PREV:
|
case operations.TAB_PREV:
|
||||||
return tabs.selectPrevTab(tab.index, operation.count);
|
return tabs.selectPrevTab(tab.index, operation.count);
|
||||||
case operations.TABS_NEXT:
|
case operations.TAB_NEXT:
|
||||||
return tabs.selectNextTab(tab.index, operation.count);
|
return tabs.selectNextTab(tab.index, operation.count);
|
||||||
case operations.TABS_RELOAD:
|
case operations.TAB_RELOAD:
|
||||||
return tabs.reload(tab, operation.cache);
|
return tabs.reload(tab, operation.cache);
|
||||||
case operations.ZOOM_IN:
|
case operations.ZOOM_IN:
|
||||||
return zooms.zoomIn();
|
return zooms.zoomIn();
|
||||||
|
@ -22,21 +22,21 @@ const exec = (operation, tab) => {
|
||||||
return zooms.zoomOut();
|
return zooms.zoomOut();
|
||||||
case operations.ZOOM_NEUTRAL:
|
case operations.ZOOM_NEUTRAL:
|
||||||
return zooms.neutral();
|
return zooms.neutral();
|
||||||
case operations.COMMAND_OPEN:
|
case operations.COMMAND_SHOW:
|
||||||
return consoleActions.showCommand('');
|
return consoleActions.showCommand('');
|
||||||
case operations.COMMAND_TABS_OPEN:
|
case operations.COMMAND_SHOW_OPEN:
|
||||||
if (operation.alter) {
|
if (operation.alter) {
|
||||||
// alter url
|
// alter url
|
||||||
return consoleActions.showCommand('open ' + tab.url);
|
return consoleActions.showCommand('open ' + tab.url);
|
||||||
}
|
}
|
||||||
return consoleActions.showCommand('open ');
|
return consoleActions.showCommand('open ');
|
||||||
case operations.COMMAND_TABS_NEW:
|
case operations.COMMAND_SHOW_TABOPEN:
|
||||||
if (operation.alter) {
|
if (operation.alter) {
|
||||||
// alter url
|
// alter url
|
||||||
return consoleActions.showCommand('tabopen ' + tab.url);
|
return consoleActions.showCommand('tabopen ' + tab.url);
|
||||||
}
|
}
|
||||||
return consoleActions.showCommand('tabopen ');
|
return consoleActions.showCommand('tabopen ');
|
||||||
case operations.COMMAND_BUFFER:
|
case operations.COMMAND_SHOW_BUFFER:
|
||||||
return consoleActions.showCommand('buffer ');
|
return consoleActions.showCommand('buffer ');
|
||||||
default:
|
default:
|
||||||
return browser.tabs.sendMessage(tab.id, {
|
return browser.tabs.sendMessage(tab.id, {
|
||||||
|
|
39
src/background/default-settings.js
Normal file
39
src/background/default-settings.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
export default `{
|
||||||
|
"keymaps": {
|
||||||
|
"0": { "type": "scroll.home" },
|
||||||
|
":": { "type": "command.show" },
|
||||||
|
"o": { "type": "command.show.open", "alter": false },
|
||||||
|
"O": { "type": "command.show.open", "alter": true },
|
||||||
|
"t": { "type": "command.show.tabopen", "alter": false },
|
||||||
|
"T": { "type": "command.show.tabopen", "alter": true },
|
||||||
|
"b": { "type": "command.show.buffer" },
|
||||||
|
"k": { "type": "scroll.lines", "count": -1 },
|
||||||
|
"j": { "type": "scroll.lines", "count": 1 },
|
||||||
|
"<C-E>": { "type": "scroll.lines", "count": -1 },
|
||||||
|
"<C-Y>": { "type": "scroll.lines", "count": 1 },
|
||||||
|
"<C-U>": { "type": "scroll.pages", "count": -0.5 },
|
||||||
|
"<C-D>": { "type": "scroll.pages", "count": 0.5 },
|
||||||
|
"<C-B>": { "type": "scroll.pages", "count": -1 },
|
||||||
|
"<C-F>": { "type": "scroll.pages", "count": 1 },
|
||||||
|
"gg": { "type": "scroll.top" },
|
||||||
|
"G": { "type": "scroll.bottom" },
|
||||||
|
"$": { "type": "scroll.end" },
|
||||||
|
"d": { "type": "tabs.close" },
|
||||||
|
"u": { "type": "tabs.reopen" },
|
||||||
|
"h": { "type": "tabs.prev", "count": 1 },
|
||||||
|
"l": { "type": "tabs.next", "count": 1 },
|
||||||
|
"r": { "type": "tabs.reload", "cache": false },
|
||||||
|
"R": { "type": "tabs.reload", "cache": true },
|
||||||
|
"zi": { "type": "zoom.in" },
|
||||||
|
"zo": { "type": "zoom.out" },
|
||||||
|
"zz": { "type": "zoom.neutral" },
|
||||||
|
"f": { "type": "follow.start", "newTab": false },
|
||||||
|
"F": { "type": "follow.start", "newTab": true },
|
||||||
|
"H": { "type": "navigate.history.prev" },
|
||||||
|
"L": { "type": "navigate.history.next" },
|
||||||
|
"[[": { "type": "navigate.link.prev" },
|
||||||
|
"]]": { "type": "navigate.link.next" },
|
||||||
|
"gu": { "type": "navigate.parent" },
|
||||||
|
"gU": { "type": "navigate.root" }
|
||||||
|
}
|
||||||
|
}`;
|
|
@ -41,7 +41,7 @@ backgroundStore.subscribe((sender) => {
|
||||||
|
|
||||||
const keyQueueChanged = (state, sender) => {
|
const keyQueueChanged = (state, sender) => {
|
||||||
let prefix = keys.asKeymapChars(state.input.keys);
|
let prefix = keys.asKeymapChars(state.input.keys);
|
||||||
let matched = Object.keys(keys.defaultKeymap).filter((keyStr) => {
|
let matched = Object.keys(state.input.keymaps).filter((keyStr) => {
|
||||||
return keyStr.startsWith(prefix);
|
return keyStr.startsWith(prefix);
|
||||||
});
|
});
|
||||||
if (matched.length === 0) {
|
if (matched.length === 0) {
|
||||||
|
@ -51,11 +51,19 @@ const keyQueueChanged = (state, sender) => {
|
||||||
matched.length === 1 && prefix !== matched[0]) {
|
matched.length === 1 && prefix !== matched[0]) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
let action = keys.defaultKeymap[matched];
|
let action = state.input.keymaps[matched];
|
||||||
backgroundStore.dispatch(operationActions.exec(action, sender.tab), sender);
|
backgroundStore.dispatch(operationActions.exec(action, sender.tab), sender);
|
||||||
backgroundStore.dispatch(inputActions.clearKeys(), sender);
|
backgroundStore.dispatch(inputActions.clearKeys(), sender);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const reloadSettings = () => {
|
||||||
|
browser.storage.local.get('settings').then((value) => {
|
||||||
|
let settings = JSON.parse(value.settings.json);
|
||||||
|
let action = inputActions.setKeymaps(settings.keymaps);
|
||||||
|
backgroundStore.dispatch(action);
|
||||||
|
}, console.error);
|
||||||
|
};
|
||||||
|
|
||||||
const handleMessage = (message, sender) => {
|
const handleMessage = (message, sender) => {
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case messages.KEYDOWN:
|
case messages.KEYDOWN:
|
||||||
|
@ -77,6 +85,8 @@ const handleMessage = (message, sender) => {
|
||||||
case messages.CONSOLE_CHANGEED:
|
case messages.CONSOLE_CHANGEED:
|
||||||
return backgroundStore.dispatch(
|
return backgroundStore.dispatch(
|
||||||
commandActions.complete(message.text), sender);
|
commandActions.complete(message.text), sender);
|
||||||
|
case messages.SETTINGS_RELOAD:
|
||||||
|
return reloadSettings();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,3 +97,9 @@ browser.runtime.onMessage.addListener((message, sender) => {
|
||||||
backgroundStore.dispatch(consoleActions.showError(e.message), sender);
|
backgroundStore.dispatch(consoleActions.showError(e.message), sender);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const initializeSettings = () => {
|
||||||
|
reloadSettings();
|
||||||
|
};
|
||||||
|
|
||||||
|
initializeSettings();
|
||||||
|
|
|
@ -1,43 +1,3 @@
|
||||||
import operations from '../operations';
|
|
||||||
|
|
||||||
const defaultKeymap = {
|
|
||||||
':': { type: operations.COMMAND_OPEN },
|
|
||||||
'o': { type: operations.COMMAND_TABS_OPEN, alter: false },
|
|
||||||
'O': { type: operations.COMMAND_TABS_OPEN, alter: true },
|
|
||||||
't': { type: operations.COMMAND_TABS_NEW, alter: false },
|
|
||||||
'T': { type: operations.COMMAND_TABS_NEW, alter: true },
|
|
||||||
'b': { type: operations.COMMAND_BUFFER },
|
|
||||||
'k': { type: operations.SCROLL_LINES, count: -1 },
|
|
||||||
'j': { type: operations.SCROLL_LINES, count: 1 },
|
|
||||||
'<C-E>': { type: operations.SCROLL_LINES, count: -1 },
|
|
||||||
'<C-Y>': { type: operations.SCROLL_LINES, count: 1 },
|
|
||||||
'<C-U>': { type: operations.SCROLL_PAGES, count: -0.5 },
|
|
||||||
'<C-D>': { type: operations.SCROLL_PAGES, count: 0.5 },
|
|
||||||
'<C-B>': { type: operations.SCROLL_PAGES, count: -1 },
|
|
||||||
'<C-F>': { type: operations.SCROLL_PAGES, count: 1 },
|
|
||||||
'gg': { type: operations.SCROLL_TOP },
|
|
||||||
'G': { type: operations.SCROLL_BOTTOM },
|
|
||||||
'0': { type: operations.SCROLL_LEFT },
|
|
||||||
'$': { type: operations.SCROLL_RIGHT },
|
|
||||||
'd': { type: operations.TABS_CLOSE },
|
|
||||||
'u': { type: operations.TABS_REOPEN },
|
|
||||||
'h': { type: operations.TABS_PREV, count: 1 },
|
|
||||||
'l': { type: operations.TABS_NEXT, count: 1 },
|
|
||||||
'r': { type: operations.TABS_RELOAD, cache: false },
|
|
||||||
'R': { type: operations.TABS_RELOAD, cache: true },
|
|
||||||
'zi': { type: operations.ZOOM_IN },
|
|
||||||
'zo': { type: operations.ZOOM_OUT },
|
|
||||||
'zz': { type: operations.ZOOM_NEUTRAL },
|
|
||||||
'f': { type: operations.FOLLOW_START, newTab: false },
|
|
||||||
'F': { type: operations.FOLLOW_START, newTab: true },
|
|
||||||
'H': { type: operations.NAVIGATE_HISTORY_PREV },
|
|
||||||
'L': { type: operations.NAVIGATE_HISTORY_NEXT },
|
|
||||||
'[[': { type: operations.NAVIGATE_LINK_PREV },
|
|
||||||
']]': { type: operations.NAVIGATE_LINK_NEXT },
|
|
||||||
'gu': { type: operations.NAVIGATE_PARENT },
|
|
||||||
'gU': { type: operations.NAVIGATE_ROOT },
|
|
||||||
};
|
|
||||||
|
|
||||||
const asKeymapChars = (keys) => {
|
const asKeymapChars = (keys) => {
|
||||||
return keys.map((k) => {
|
return keys.map((k) => {
|
||||||
let c = String.fromCharCode(k.code);
|
let c = String.fromCharCode(k.code);
|
||||||
|
@ -58,4 +18,4 @@ const asCaretChars = (keys) => {
|
||||||
}).join('');
|
}).join('');
|
||||||
};
|
};
|
||||||
|
|
||||||
export { defaultKeymap, asKeymapChars, asCaretChars };
|
export { asKeymapChars, asCaretChars };
|
||||||
|
|
|
@ -85,9 +85,9 @@ const execOperation = (operation) => {
|
||||||
return scrolls.scrollTop(window);
|
return scrolls.scrollTop(window);
|
||||||
case operations.SCROLL_BOTTOM:
|
case operations.SCROLL_BOTTOM:
|
||||||
return scrolls.scrollBottom(window);
|
return scrolls.scrollBottom(window);
|
||||||
case operations.SCROLL_LEFT:
|
case operations.SCROLL_HOME:
|
||||||
return scrolls.scrollLeft(window);
|
return scrolls.scrollLeft(window);
|
||||||
case operations.SCROLL_RIGHT:
|
case operations.SCROLL_END:
|
||||||
return scrolls.scrollRight(window);
|
return scrolls.scrollRight(window);
|
||||||
case operations.FOLLOW_START:
|
case operations.FOLLOW_START:
|
||||||
return startFollows(operation.newTab);
|
return startFollows(operation.newTab);
|
||||||
|
|
|
@ -8,5 +8,7 @@ export default {
|
||||||
|
|
||||||
KEYDOWN: 'keydown',
|
KEYDOWN: 'keydown',
|
||||||
|
|
||||||
OPEN_URL: 'open.url'
|
OPEN_URL: 'open.url',
|
||||||
|
|
||||||
|
SETTINGS_RELOAD: 'settings.reload',
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,17 +1,22 @@
|
||||||
export default {
|
export default {
|
||||||
// Command
|
// Command
|
||||||
COMMAND_OPEN: 'cmd.open',
|
COMMAND_SHOW: 'command.show',
|
||||||
COMMAND_TABS_OPEN: 'cmd.tabs.open',
|
COMMAND_SHOW_OPEN: 'command.show.open',
|
||||||
COMMAND_TABS_NEW: 'cmd.tabs.new',
|
COMMAND_SHOW_TABOPEN: 'command.show.tabopen',
|
||||||
COMMAND_BUFFER: 'cmd.buffer',
|
COMMAND_SHOW_BUFFER: 'command.show.buffer',
|
||||||
|
|
||||||
|
// Scrolls
|
||||||
SCROLL_LINES: 'scroll.lines',
|
SCROLL_LINES: 'scroll.lines',
|
||||||
SCROLL_PAGES: 'scroll.pages',
|
SCROLL_PAGES: 'scroll.pages',
|
||||||
SCROLL_TOP: 'scroll.top',
|
SCROLL_TOP: 'scroll.top',
|
||||||
SCROLL_BOTTOM: 'scroll.bottom',
|
SCROLL_BOTTOM: 'scroll.bottom',
|
||||||
SCROLL_LEFT: 'scroll.left',
|
SCROLL_HOME: 'scroll.home',
|
||||||
SCROLL_RIGHT: 'scroll.right',
|
SCROLL_END: 'scroll.end',
|
||||||
|
|
||||||
|
// Follows
|
||||||
FOLLOW_START: 'follow.start',
|
FOLLOW_START: 'follow.start',
|
||||||
|
|
||||||
|
// Navigations
|
||||||
NAVIGATE_HISTORY_PREV: 'navigate.history.prev',
|
NAVIGATE_HISTORY_PREV: 'navigate.history.prev',
|
||||||
NAVIGATE_HISTORY_NEXT: 'navigate.history.next',
|
NAVIGATE_HISTORY_NEXT: 'navigate.history.next',
|
||||||
NAVIGATE_LINK_PREV: 'navigate.link.prev',
|
NAVIGATE_LINK_PREV: 'navigate.link.prev',
|
||||||
|
@ -19,12 +24,14 @@ export default {
|
||||||
NAVIGATE_PARENT: 'navigate.parent',
|
NAVIGATE_PARENT: 'navigate.parent',
|
||||||
NAVIGATE_ROOT: 'navigate.root',
|
NAVIGATE_ROOT: 'navigate.root',
|
||||||
|
|
||||||
// Background
|
// Tabs
|
||||||
TABS_CLOSE: 'tabs.close',
|
TAB_CLOSE: 'tabs.close',
|
||||||
TABS_REOPEN: 'tabs.reopen',
|
TAB_REOPEN: 'tabs.reopen',
|
||||||
TABS_PREV: 'tabs.prev',
|
TAB_PREV: 'tabs.prev',
|
||||||
TABS_NEXT: 'tabs.next',
|
TAB_NEXT: 'tabs.next',
|
||||||
TABS_RELOAD: 'tabs.reload',
|
TAB_RELOAD: 'tabs.reload',
|
||||||
|
|
||||||
|
// Zooms
|
||||||
ZOOM_IN: 'zoom.in',
|
ZOOM_IN: 'zoom.in',
|
||||||
ZOOM_OUT: 'zoom.out',
|
ZOOM_OUT: 'zoom.out',
|
||||||
ZOOM_NEUTRAL: 'zoom.neutral',
|
ZOOM_NEUTRAL: 'zoom.neutral',
|
||||||
|
|
|
@ -2,6 +2,7 @@ import actions from '../actions';
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
keys: [],
|
keys: [],
|
||||||
|
keymaps: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function reducer(state = defaultState, action = {}) {
|
export default function reducer(state = defaultState, action = {}) {
|
||||||
|
@ -19,6 +20,11 @@ export default function reducer(state = defaultState, action = {}) {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
keys: [],
|
keys: [],
|
||||||
});
|
});
|
||||||
|
case actions.INPUT_SET_KEYMAPS:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
keymaps: action.keymaps,
|
||||||
|
keys: [],
|
||||||
|
});
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
22
src/settings/index.js
Normal file
22
src/settings/index.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import './settings.scss';
|
||||||
|
import messages from '../messages';
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
let form = document.getElementById('vimvixen-settings-form');
|
||||||
|
form.addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
browser.storage.local.set({
|
||||||
|
settings: {
|
||||||
|
json: e.target.elements['plain-json'].value
|
||||||
|
}
|
||||||
|
}).then(() => {
|
||||||
|
return browser.runtime.sendMessage({
|
||||||
|
type: messages.SETTINGS_RELOAD
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.storage.local.get('settings').then((value) => {
|
||||||
|
form.elements['plain-json'].value = value.settings.json;
|
||||||
|
}, console.error);
|
||||||
|
});
|
18
src/settings/settings.html
Normal file
18
src/settings/settings.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset='utf-8'>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Configure</h1>
|
||||||
|
|
||||||
|
<h2>Home page</h2>
|
||||||
|
<form id='vimvixen-settings-form' class='vimvixen-settings-form'>
|
||||||
|
<label for='load-from-json'>Load from JSON:</label>
|
||||||
|
<textarea name='plain-json' spellcheck='false'></textarea>
|
||||||
|
|
||||||
|
<button type='submit'>Save</button>
|
||||||
|
</form>
|
||||||
|
<script src='settings.js'></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
8
src/settings/settings.scss
Normal file
8
src/settings/settings.scss
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
.vimvixen-settings-form {
|
||||||
|
textarea[name=plain-json] {
|
||||||
|
font-family: monospace;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 64ex;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ const dist = path.resolve(__dirname, 'build');
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: {
|
entry: {
|
||||||
index: path.join(src, 'content'),
|
index: path.join(src, 'content'),
|
||||||
|
settings: path.join(src, 'settings'),
|
||||||
background: path.join(src, 'background'),
|
background: path.join(src, 'background'),
|
||||||
console: path.join(src, 'console', 'console.js')
|
console: path.join(src, 'console', 'console.js')
|
||||||
},
|
},
|
||||||
|
@ -46,6 +47,11 @@ module.exports = {
|
||||||
template: path.join(src, 'console', 'console.html'),
|
template: path.join(src, 'console', 'console.html'),
|
||||||
filename: path.join(dist, 'console.html'),
|
filename: path.join(dist, 'console.html'),
|
||||||
inject: false
|
inject: false
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: path.join(src, 'settings', 'settings.html'),
|
||||||
|
filename: path.join(dist, 'settings.html'),
|
||||||
|
inject: false
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
Reference in a new issue