Skip to content

Commit

Permalink
Merge pull request #7 from sculpt0r/feat/keyboard-shortcuts
Browse files Browse the repository at this point in the history
[WIP]build(deps): instal chrome extension types
  • Loading branch information
sculpt0r authored Aug 18, 2024
2 parents b47d71f + d3b5c04 commit ac49c25
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 47 deletions.
29 changes: 18 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
[![CI - test](https://github.com/sculpt0r/vim-browser-extension/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/sculpt0r/vim-browser-extension/actions/workflows/test.yml)

# vim-browser-extension
Browser extension to allow vim keys for text editing.

Browser extension which allows Vim keys for text editing inside `textarea` HTML elements.
It uses only pure DOM API.

# why it is different than other vim plugins
Other famous plugins use vim keys to navigate in browser/web page. This plugin allows to use vim keys in text editing inputs!

Other famous plugins use vim keys to navigate in the browser/web page. This plugin allows to use vim keys in text editing inputs!

# enable vim keys
Plugins functionality can be toggled with a special key combination:
- `alt` + `v` on windows / linux
- `right option` + `left cmd` on macOS

It is because sometimes you may don't want to `vim-typer` interfere with another vim-like plugin.
Plugin functionality can be toggled with a special key combination:

- `alt` + `v` on windows / linux
- `Command` + `Control` + `v` on macOS

It is because sometimes you may not want to `vim-typer` interfere with another vim-like plugin.

Please look at the indicator. When it becomes green: the plugin functions are available.

# supported keys

Currently, `vim-typer` supports:
- `h`,`j`,`k`,`l`
- `e`
- `w` has early support
- more are planned

- `h`,`j`,`k`,`l`
- `e`
- `w` has early support
- more are planned

# Development

work with `src` and `tests` folder. Everything is bundled to the `src_js` directory with rollup `npx rollup --config`
work with `src` and `tests` folder. Everything is bundled to the `src_js` directory with `npm run build`
67 changes: 65 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vim-browser-extension",
"version": "1.1.0",
"version": "1.1.1",
"description": "Browser extension to allow vim keys for text editing.",
"browser": "src_js/plugin.js",
"scripts": {
Expand All @@ -26,6 +26,7 @@
"@rollup/plugin-node-resolve": "^13.3.0",
"@rollup/plugin-typescript": "^8.3.4",
"@stryker-mutator/core": "^6.1.2",
"@types/chrome": "^0.0.270",
"@typescript-eslint/eslint-plugin": "^5.33.0",
"@typescript-eslint/parser": "^5.33.0",
"archiver": "^5.3.1",
Expand Down
1 change: 1 addition & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default {
copy( {
targets: [
{ src: 'src/manifest.json', dest: 'src_js/' },
{ src: 'src/worker.js', dest: 'src_js/' }
]
} ),
typescript()
Expand Down
Empty file removed src/background.js
Empty file.
71 changes: 47 additions & 24 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,68 @@ import { InsertMode } from './insert-mode';
import { EmptyMode } from './empty-mode';

const modeMgr = new ModeManager();

let indicator;
let currentElement;
let abortCtrlBlurHandler;

function HandlePluginToggle( e : KeyboardEvent ) : void {
if (
isEditableSupported() &&
(
// Alt + v
( e.altKey && e.key === 'v' ) ||
// On mac: left Cmd key + option key
( e.altKey && ( e.key === 'Meta' && e.code === 'MetaLeft' ) )
)
) {
if ( modeMgr.anyMode() ) {
modeMgr.changeMode( new EmptyMode() );
setIndicator( false );
//unpin all listeners etc...
} else {
modeMgr.changeMode( new NavigationMode() );
setIndicator( true );
}
chrome.runtime.onMessage.addListener( ( message, sender, sendResponse ) => {
TogglePlugin();
} );

function TogglePlugin() {
const inAnyMode = modeMgr.anyMode();

// Prevent toggle ON, but allow OFF.
if ( !isEditableSupported() && !inAnyMode) {
return;
}

const blurHanlder = () => {
modeMgr.changeMode( new EmptyMode() );
setIndicator( false );

abortCtrlBlurHandler.abort();
}

if ( inAnyMode ) {
modeMgr.changeMode( new EmptyMode() );
abortCtrlBlurHandler.abort();
} else {
abortCtrlBlurHandler = new AbortController();
currentElement = document.activeElement;
currentElement.addEventListener(
'blur',
blurHanlder,
{ signal: abortCtrlBlurHandler.signal }
)

modeMgr.changeMode( new NavigationMode() );
}

setIndicator( !inAnyMode );
}

function isEditableSupported() : boolean {
return document.activeElement.tagName === 'TEXTAREA';
}

function keydownListener( e : KeyboardEvent ) : void {
if ( modeMgr.anyMode() ) {
if ( e.key === 'Escape' && ! ( modeMgr.currentMode instanceof NavigationMode ) ) {
modeMgr.changeMode( new NavigationMode() );
}

if ( modeMgr.currentMode instanceof NavigationMode && ( e.key === 'i' || e.key === 'a' ) ) {
if (
modeMgr.currentMode instanceof NavigationMode &&
( e.key === 'i' || e.key === 'a' )
) {
modeMgr.changeMode( new InsertMode( e.key === 'a' ) );
e.preventDefault();
}
}
}

function isEditableSupported() : boolean {
return document.activeElement.tagName === 'TEXTAREA';
}

function createIndicator() : void {
indicator = document.createElement( 'div' );
indicator.style.width = indicator.style.height = '5px';
Expand All @@ -56,7 +79,7 @@ function setIndicator( isActive : boolean ) {
indicator.style.background = isActive ? 'green' : 'red';
}

document.addEventListener( 'keydown', HandlePluginToggle );
document.addEventListener( 'keydown', keydownListener );

window.addEventListener( 'DOMContentLoaded', function( event ) {
createIndicator();
Expand Down
15 changes: 14 additions & 1 deletion src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,18 @@
"run_at": "document_start",
"all_frames": true
}
]
],
"commands": {
"toggle-vim-mode": {
"suggested_key": {
"default": "Alt+V",
"mac": "Command+MacCtrl+V"
},
"description": "Toggle plugin activation."
}
},
"background": {
"service_worker": "worker.js",
"type": "module"
}
}
25 changes: 25 additions & 0 deletions src/worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
chrome.commands.onCommand.addListener(async function (command) {
switch (command) {
case "toggle-vim-mode":
const id = (await getCurrentTab()).id;

if (typeof id === "undefined") {
console.log("Cannot find active tab...");
break;
}
chrome.tabs.sendMessage(id, { msg: "please toggle plugin" });
break;
default:
console.log(`Command ${command} not found`);
}
});

/**
* https://developer.chrome.com/docs/extensions/reference/api/tabs#get_the_current_tab
*/
async function getCurrentTab() {
let queryOptions = { active: true, lastFocusedWindow: true };
// `tab` will either be a `tabs.Tab` instance or `undefined`.
let [tab] = await chrome.tabs.query(queryOptions);
return tab;
}
21 changes: 13 additions & 8 deletions test.html
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible"
content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<textarea style="width: 75%; height: 450px">abc
<textarea style="width: 75%; height: 250px">abc
de f
ghj
oko loko
okoloko
</textarea>
<textarea style="width: 75%; height: 450px"></textarea>
<br>
Contenteditable: <div contenteditable="true">aaa</div>
<textarea style="width: 75%; height: 250px"></textarea>
<br>
Contenteditable: <div contenteditable="true">aaa</div>
</body>

</html>

0 comments on commit ac49c25

Please sign in to comment.