Skip to content
This repository has been archived by the owner on Nov 7, 2022. It is now read-only.

[WIP] Implement Custom Command #79

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/lib/trie.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,15 @@ export default class Trie {
*/
advance = input => {
const next = this.curNode[input]
switch (typeof next) {
case 'undefined':
if (typeof next == 'undefined') {
this.curNode = this.root
return this.root[input] === undefined
? Trie.INVALID
: this.advance(input)
case 'object':
} else if (next._TrieNode === true) {
this.curNode = next
return Trie.INTERNAL
default:
} else {
this.curNode = this.root
return next
}
Expand Down
12 changes: 12 additions & 0 deletions src/modes/command/client/commands/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,15 @@ export function passAllKeys (event) {
event.passKeyType = 'all'
return 'Pass'
}

export function customCommand (event, data) {
console.log('Custom command', data)

let global_eval = eval; // This causes the eval to happen in the global scope.
let func = global_eval(`
(event) => {
${data.source}
}
`);
return func(event);
}
3 changes: 2 additions & 1 deletion src/modes/command/client/trie.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export function advanceInputTrie (event) {
default:
event.preventDefault()
event.stopImmediatePropagation()
return commands[command](event) || 'Same'
let implementation = commands[command.command]
return implementation(event, command) || 'Same'
}
}
10 changes: 10 additions & 0 deletions src/options/CustomCommands/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "Custom Commands",
"options": [
{
"type": "customcommands",
"key": "customCommands",
"default": []
}
]
}
11 changes: 11 additions & 0 deletions src/options/CustomCommands/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "Custom Commands",
"profiles": [
{
"name": "default",
"options": {
"customCommands": []
}
}
]
}
8 changes: 8 additions & 0 deletions src/options/CustomCommands/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { getAttributes } from 'lib/util'

export default (options, config) => {
const backgroundOptions = {}
const clientOptions = {}
const errors = {}
return { backgroundOptions, clientOptions, errors }
}
4 changes: 4 additions & 0 deletions src/options/Keybindings/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,10 @@
"label": "Activate Clipboard in Incognito Window",
"key": "clipboardIncognitoWindow",
"default": []
},
{
"type": "header",
"label": "Custom"
}
]
}
17 changes: 11 additions & 6 deletions src/options/Keybindings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,21 @@ export default (options, config) => {
}

function keybindingsPerMode (options, config) {
const keybindings = {}
const keybindings = { command: [], pass: [] }
config.forEach(item => {
if (item.type === 'keybinding') {
const { mode = 'command', key } = item
if (keybindings[mode]) {
keybindings[mode][key] = options[key]
} else {
keybindings[mode] = { [key]: options[key] }
}
keybindings[mode].push({ bindings: options[key], command: key })
}
})
options.customCommands.forEach((item, i) => {
const { mode = 'command', source, bindings } = item
keybindings[mode].push({
bindings,
command: "customCommand",
source,
preventDefault: true,
})
})
return keybindings
}
24 changes: 14 additions & 10 deletions src/options/Keybindings/trie.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ import { validKeyboardEvent, keyboardEventString } from 'lib/keys'
* Given an object mapping commands to their key bindings,
* returns the trie representation.
*/
export function generateCommandTrie (commandMap) {
return JSONTrie(bindingsList(commandMap))
export function generateCommandTrie (commandList) {
return JSONTrie(bindingsList(commandList))
}

/**
* Given an object mapping commands to their key bindings,
* returns an array of (commands, binding).
* A command may have more than one binding, or none at all.
*/
function bindingsList (bindingsMap) {
function bindingsList (commandList) {
const out = []
for (const [command, bindings] of Object.entries(bindingsMap)) {
if (bindings === undefined) {
throw Error(`A profile is missing bindings for command ${command}`)
for (const command of commandList) {
if (command.bindings === undefined) {
throw Error(`A profile is missing bindings for command ${command.command}`)
}
for (const binding of bindings) {
for (const binding of command.bindings) {
const keySequence = binding.map(key => {
try {
validKeyboardEvent(key)
Expand All @@ -46,11 +46,15 @@ function JSONTrie (bindings) {
return trie
}

function isTrieNode (thing) {
return typeof thing == 'object' && thing._TrieNode === true
}

function addToTrie (trie, i, key, value, binding) {
if (key.length === 0) {
throw Error(`${value} has a 0 length key binding`)
} else if (i === key.length - 1) {
if (trie.hasOwnProperty(key[i])) {
if (isTrieNode(trie[key[i]])) {
throw {
message: `${firstLeafValue(
trie[key[i]]
Expand All @@ -63,7 +67,7 @@ function addToTrie (trie, i, key, value, binding) {
trie[key[i]] = value
}
} else {
if (trie.hasOwnProperty(key[i])) {
if (isTrieNode(trie[key[i]])) {
if (typeof trie[key[i]] === 'object') {
addToTrie(trie[key[i]], i + 1, key, value)
} else {
Expand All @@ -75,7 +79,7 @@ function addToTrie (trie, i, key, value, binding) {
}
}
} else {
addToTrie((trie[key[i]] = {}), i + 1, key, value)
addToTrie((trie[key[i]] = {_TrieNode: true}), i + 1, key, value)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Component, h } from 'preact'
import TextArea from '../TextArea'
import Keybinding from '../Keybinding'

const DEFAULT = {
source: 'document.body.style.backgroundColor = "blue"',
bindings: [],
}

class CustomCommand extends Component {
render ({ command, values, onChange }) {
return (
<li>
<textarea
id="multi-line"
class="mdc-textfield__input"
value={command.source}
rows={command.source.split('\n').length + 3}
cols="40"
onChange={e => onChange({ ...command, source: e.target.value })}/>
<Keybinding
label="Key Bindings"
key="foobar"
value={command.bindings}
values={values}
physicalKeys={false}
ignoreModifierKeys={false}
onChange={b => onChange({ ...command, bindings: b })}/>
<button onClick={e => { onChange(undefined) }}>Delete Custom Command</button>
</li>
)
}
}

export default class CustomCommands extends Component {
render ({ key, value, values, onChange, ...rest }) {
return (
<li>
<ul>
{value &&
value.map((cmd, i, cmds) => {
let cmdChange = cmd => {
if (!cmd) {
// Remove the command from the list.
onChange(cmds.slice(0, i).concat(cmds.slice(i+1)))
} else {
// Update the command.
let updated = cmds.slice()
updated[i] = cmd
onChange(updated)
}
}
return <CustomCommand command={cmd} values={values} onChange={cmdChange} />
})}
</ul>
<button onClick={e => onChange([...value, DEFAULT])}>Add Custom Command</button>
</li>
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ToggleButton from './ToggleButton'
import Switch from './Switch'
import Checkbox from './Checkbox'
import Color from './Color'
import CustomCommands from './CustomCommands'
import Text from './Text'
import Number from './Number'
import TextArea from './TextArea'
Expand Down Expand Up @@ -34,6 +35,8 @@ function optionWidgetByType (type) {
return Switch
case 'color':
return Color
case 'customcommands':
return CustomCommands
case 'text':
return Text
case 'number':
Expand Down
6 changes: 4 additions & 2 deletions src/storage/procedures.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ export async function storageUpdateProcedure (previousVersion) {
await storageSet({ ready: true })
} catch (e) {
console.error(
'FATAL ERROR: UPDATE FAILED. DELETE THEN REINSTALL THE EXTENSION.',
'FATAL ERROR: UPDATE FAILED. WIPING STORAGE AND REINITIALIZING.',
e
)
await storageClear()
await storageInstallProcedure()
}
}

Expand Down Expand Up @@ -228,7 +230,7 @@ async function deleteConfig () {
/**
* Accounts for the case where a built-in profile introduced in an update conflicts with
* a custom profile already installed. Renames the custom profile, active profile, and action
* if a conflic is detected.
* if a conflict is detected.
*/
async function renameCustomProfilesAndOptions () {
const {
Expand Down
4 changes: 3 additions & 1 deletion src/storage/transform.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import transformGeneral from 'options/General'
import transformKeybindings from 'options/Keybindings'
import transformAppearance from 'options/Appearance'
import transformCustomCommands from 'options/CustomCommands'
const transforms = {
General: transformGeneral,
Keybindings: transformKeybindings,
Appearance: transformAppearance
Appearance: transformAppearance,
CustomCommands: transformCustomCommands
}
export const categories = Object.keys(transforms)

Expand Down