Multiple improvements

- Focus fighting!
- Toast for "invisible" actions
- Bookmarks fix
This commit is contained in:
Alyssa X 2022-01-18 09:56:19 +00:00
parent e8a0ef5965
commit a03877ea09
7 changed files with 547 additions and 13 deletions

BIN
src/assets/.DS_Store vendored Normal file

Binary file not shown.

3
src/assets/check.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.5 17C13.1944 17 17 13.1944 17 8.5C17 3.80558 13.1944 0 8.5 0C3.80558 0 0 3.80558 0 8.5C0 13.1944 3.80558 17 8.5 17ZM13.2337 6.97078C13.6242 6.58025 13.6242 5.94709 13.2337 5.55657C12.8431 5.16604 12.21 5.16604 11.8194 5.55657L7.60549 9.77051L6.07576 8.24078C5.68524 7.85025 5.05207 7.85025 4.66155 8.24078C4.27102 8.6313 4.27102 9.26446 4.66155 9.65499L6.89839 11.8918C7.08592 12.0794 7.34028 12.1847 7.60549 12.1847C7.87071 12.1847 8.12506 12.0794 8.3126 11.8918L13.2337 6.97078Z" fill="#6068D2"/>
</svg>

After

Width:  |  Height:  |  Size: 654 B

View file

@ -2,7 +2,6 @@
.omni-extension ::-webkit-scrollbar {
width: 10px;
height: 10px;
transition: 1s;
}
.omni-extension ::-webkit-scrollbar-thumb {
@ -10,13 +9,11 @@
background-clip: padding-box;
border: 2px solid transparent;
border-radius: 5px;
transition: 1s;
}
.omni-extension ::-webkit-scrollbar-thumb:vertical:hover,
.omni-extension ::-webkit-scrollbar-thumb:horizontal:hover {
background-color: rgb(110, 110, 110);
transition: 0.3s;
}
.omni-extension ::-webkit-scrollbar-track {
@ -345,10 +342,38 @@
.omni-extension .omni-keys span {
margin-left: 3px;
}
#body-wrap {
width: calc(100% - 350px);
overflow: auto;
position: absolute;
top: 0px;
#omni-extension-toast {
text-align: center;
font-family: Inter;
font-weight: 500;
font-size: 14px;
position: fixed;
width: fit-content;
color: var(--text);
bottom: 10px;
left: 0px;
right: 0px;
margin: auto;
background: var(--background);
border-radius: 5px;
height: 40px;
line-height: 40px;
display: block;
padding-left: 10px;
padding-right: 10px;
visibility: hidden;
opacity: 0;
transition: all 0.2s cubic-bezier(0.05, 0.03, 0.35, 1);
z-index: 99999999;
}
#omni-extension-toast img {
display: inline-block;
margin-right: 5px;
vertical-align: middle;
margin-bottom: 2px;
}
.omni-show-toast {
bottom: 20px !important;
opacity: 1 !important;
visibility: visible !important;
}

View file

@ -18,5 +18,6 @@
</div>
<div id="omni-overlay"></div>
</div>
<div id="omni-extension-toast"><img src=''><span>The action has been successful</span></div>
</body>
</html>

View file

@ -14,6 +14,9 @@ $(document).ready(() => {
$.get(chrome.runtime.getURL('/content.html'), (data) => {
$(data).appendTo('body');
// Get checkmark image for toast
$("#omni-extension-toast img").attr("src", chrome.runtime.getURL("assets/check.svg"));
// Request actions from the background
chrome.runtime.sendMessage({request:"get-actions"}, (response) => {
actions = response.actions;
@ -114,6 +117,8 @@ $(document).ready(() => {
$("#omni-extension").removeClass("omni-closing");
window.setTimeout(() => {
$("#omni-extension input").focus();
focusLock.on($("#omni-extension input").get(0));
$("#omni-extension input").focus();
}, 100);
});
}
@ -134,6 +139,15 @@ $(document).ready(() => {
$(this).addClass("omni-item-active");
}
// Show a toast when an action has been performed
function showToast(action) {
$("#omni-extension-toast span").html('"'+action.title+'" has been successfully performed');
$("#omni-extension-toast").addClass("omni-show-toast");
setTimeout(() => {
$(".omni-show-toast").removeClass("omni-show-toast");
}, 3000)
}
// Autocomplete commands. Since they all start with different letters, it can be the default behavior
function checkShortHand(e, value) {
var el = $(".omni-extension input");
@ -273,9 +287,15 @@ $(document).ready(() => {
chrome.runtime.sendMessage({request:"remove", type:action.type, action:action});
} else if ($(".omni-extension input").val().toLowerCase().startsWith("/history")) {
if (e.ctrlKey || e.metaKey) {
window.open($(".omni-item-active").attr("data-url"), "_self");
} else {
window.open($(".omni-item-active").attr("data-url"));
} else {
window.open($(".omni-item-active").attr("data-url"), "_self");
}
} else if ($(".omni-extension input").val().toLowerCase().startsWith("/bookmarks")) {
if (e.ctrlKey || e.metaKey) {
window.open($(".omni-item-active").attr("data-url"));
} else {
window.open($(".omni-item-active").attr("data-url"), "_self");
}
} else {
chrome.runtime.sendMessage({request:action.action, tab:action, query:$(".omni-extension input").val()});
@ -289,12 +309,13 @@ $(document).ready(() => {
break;
case "scroll-bottom":
window.scrollTo(0,document.body.scrollHeight);
showToast(action);
break;
case "scroll-top":
window.scrollTo(0,0);
break;
case "navigation":
if (e.ctrlKey) {
if (e.ctrlKey || e.metaKey) {
window.open(action.url);
} else {
window.open(action.url, "_self");
@ -327,6 +348,14 @@ $(document).ready(() => {
case "print":
window.print();
break;
case "remove-all":
case "remove-history":
case "remove-cookies":
case "remove-cache":
case "remove-local-storage":
case "remove-passwords":
showToast(action);
break;
}
}

476
src/focus.js Normal file
View file

@ -0,0 +1,476 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.focusLock = factory());
}(this, function () { 'use strict';
var toArray = function toArray(a) {
var ret = Array(a.length);
for (var i = 0; i < a.length; ++i) {
ret[i] = a[i];
}
return ret;
};
var arrayFind = function arrayFind(array, search) {
return array.filter(function (a) {
return a === search;
})[0];
};
var asArray = function asArray(a) {
return Array.isArray(a) ? a : [a];
};
var tabSort = function tabSort(a, b) {
var tabDiff = a.tabIndex - b.tabIndex;
var indexDiff = a.index - b.index;
if (tabDiff) {
if (!a.tabIndex) return 1;
if (!b.tabIndex) return -1;
}
return tabDiff || indexDiff;
};
var orderByTabIndex = function orderByTabIndex(nodes, filterNegative, keepGuards) {
return toArray(nodes).map(function (node, index) {
return {
node: node,
index: index,
tabIndex: keepGuards && node.tabIndex === -1 ? (node.dataset || {}).focusGuard ? 0 : -1 : node.tabIndex
};
}).filter(function (data) {
return !filterNegative || data.tabIndex >= 0;
}).sort(tabSort);
};
var tabbables = ['button:enabled:not([readonly])', 'select:enabled:not([readonly])', 'textarea:enabled:not([readonly])', 'input:enabled:not([readonly])', 'a[href]', 'area[href]', 'iframe', 'object', 'embed', '[tabindex]', '[contenteditable]', '[autofocus]'];
var FOCUS_GROUP = 'data-focus-lock';
var FOCUS_DISABLED = 'data-focus-lock-disabled';
var FOCUS_ALLOW = 'data-no-focus-lock';
var FOCUS_AUTO = 'data-autofocus-inside';
var queryTabbables = tabbables.join(',');
var queryGuardTabbables = queryTabbables + ', [data-focus-guard]';
var getFocusables = function getFocusables(parents, withGuards) {
return parents.reduce(function (acc, parent) {
return acc.concat(
// add all tabbables inside
toArray(parent.querySelectorAll(withGuards ? queryGuardTabbables : queryTabbables)),
// add if node is tabble itself
parent.parentNode ? toArray(parent.parentNode.querySelectorAll(tabbables.join(','))).filter(function (node) {
return node === parent;
}) : []);
}, []);
};
var getParentAutofocusables = function getParentAutofocusables(parent) {
var parentFocus = parent.querySelectorAll('[' + FOCUS_AUTO + ']');
return toArray(parentFocus).map(function (node) {
return getFocusables([node]);
}).reduce(function (acc, nodes) {
return acc.concat(nodes);
}, []);
};
var isElementHidden = function isElementHidden(computedStyle) {
if (!computedStyle || !computedStyle.getPropertyValue) {
return false;
}
return computedStyle.getPropertyValue('display') === 'none' || computedStyle.getPropertyValue('visibility') === 'hidden';
};
var isVisible = function isVisible(node) {
return !node || node === document || !isElementHidden(window.getComputedStyle(node, null)) && isVisible(node.parentNode);
};
var notHiddenInput = function notHiddenInput(node) {
return !((node.tagName === 'INPUT' || node.tagName === 'BUTTON') && (node.type === 'hidden' || node.disabled));
};
var getParents = function getParents(node) {
var parents = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
parents.push(node);
if (node.parentNode) {
getParents(node.parentNode, parents);
}
return parents;
};
var getCommonParent = function getCommonParent(nodea, nodeb) {
var parentsA = getParents(nodea);
var parentsB = getParents(nodeb);
for (var i = 0; i < parentsA.length; i += 1) {
var currentParent = parentsA[i];
if (parentsB.indexOf(currentParent) >= 0) {
return currentParent;
}
}
return false;
};
var filterFocusable = function filterFocusable(nodes) {
return toArray(nodes).filter(function (node) {
return isVisible(node);
}).filter(function (node) {
return notHiddenInput(node);
});
};
var getTabbableNodes = function getTabbableNodes(topNodes, withGuards) {
return orderByTabIndex(filterFocusable(getFocusables(topNodes, withGuards)), true, withGuards);
};
var getAllTabbableNodes = function getAllTabbableNodes(topNodes) {
return orderByTabIndex(filterFocusable(getFocusables(topNodes)), false);
};
var parentAutofocusables = function parentAutofocusables(topNode) {
return filterFocusable(getParentAutofocusables(topNode));
};
var isRadio = function isRadio(node) {
return node.tagName === 'INPUT' && node.type === 'radio';
};
var findSelectedRadio = function findSelectedRadio(node, nodes) {
return nodes.filter(isRadio).filter(function (el) {
return el.name === node.name;
}).filter(function (el) {
return el.checked;
})[0] || node;
};
var pickFirstFocus = function pickFirstFocus(nodes) {
if (nodes[0] && nodes.length > 1) {
if (isRadio(nodes[0]) && nodes[0].name) {
return findSelectedRadio(nodes[0], nodes);
}
}
return nodes[0];
};
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var filterNested = function filterNested(nodes) {
var l = nodes.length;
for (var i = 0; i < l; i += 1) {
var _loop = function _loop(j) {
if (i !== j) {
if (nodes[i].contains(nodes[j])) {
return {
v: filterNested(nodes.filter(function (x) {
return x !== nodes[j];
}))
};
}
}
};
for (var j = 0; j < l; j += 1) {
var _ret = _loop(j);
if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v;
}
}
return nodes;
};
var getTopParent = function getTopParent(node) {
return node.parentNode ? getTopParent(node.parentNode) : node;
};
var getAllAffectedNodes = function getAllAffectedNodes(node) {
var nodes = asArray(node);
return nodes.filter(Boolean).reduce(function (acc, currentNode) {
var group = currentNode.getAttribute(FOCUS_GROUP);
acc.push.apply(acc, group ? filterNested(toArray(getTopParent(currentNode).querySelectorAll('[' + FOCUS_GROUP + '="' + group + '"]:not([' + FOCUS_DISABLED + '="disabled"])'))) : [currentNode]);
return acc;
}, []);
};
var findAutoFocused = function findAutoFocused(autoFocusables) {
return function (node) {
return !!node.autofocus || node.dataset && !!node.dataset.autofocus || autoFocusables.indexOf(node) >= 0;
};
};
var newFocus = function newFocus(innerNodes, outerNodes, activeElement, lastNode, autoFocused) {
var cnt = innerNodes.length;
var firstFocus = innerNodes[0];
var lastFocus = innerNodes[cnt - 1];
// focus is inside
if (innerNodes.indexOf(activeElement) >= 0) {
return undefined;
}
var activeIndex = outerNodes.indexOf(activeElement);
var lastIndex = outerNodes.indexOf(lastNode || activeIndex);
var lastNodeInside = innerNodes.indexOf(lastNode);
var indexDiff = activeIndex - lastIndex;
var firstNodeIndex = outerNodes.indexOf(firstFocus);
var lastNodeIndex = outerNodes.indexOf(lastFocus);
// new focus
if (activeIndex === -1 || lastNodeInside === -1) {
return innerNodes.indexOf(autoFocused.length ? pickFirstFocus(autoFocused) : pickFirstFocus(innerNodes));
}
// old focus
if (!indexDiff && lastNodeInside >= 0) {
return lastNodeInside;
}
// jump out
if (indexDiff && Math.abs(indexDiff) > 1) {
return lastNodeInside;
}
// focus above lock
if (activeIndex <= firstNodeIndex) {
return cnt - 1;
}
// focus below lock
if (activeIndex > lastNodeIndex) {
return 0;
}
// index is inside tab order, but outside Lock
if (indexDiff) {
if (Math.abs(indexDiff) > 1) {
return lastNodeInside;
}
return (cnt + lastNodeInside + indexDiff) % cnt;
}
// do nothing
return undefined;
};
var getTopCommonParent = function getTopCommonParent(baseActiveElement, leftEntry, rightEntries) {
var activeElements = asArray(baseActiveElement);
var leftEntries = asArray(leftEntry);
var activeElement = activeElements[0];
var topCommon = null;
leftEntries.filter(Boolean).forEach(function (entry) {
topCommon = getCommonParent(topCommon || entry, entry) || topCommon;
rightEntries.filter(Boolean).forEach(function (subEntry) {
var common = getCommonParent(activeElement, subEntry);
if (common) {
if (!topCommon || common.contains(topCommon)) {
topCommon = common;
} else {
topCommon = getCommonParent(common, topCommon);
}
}
});
});
return topCommon;
};
var allParentAutofocusables = function allParentAutofocusables(entries) {
return entries.reduce(function (acc, node) {
return acc.concat(parentAutofocusables(node));
}, []);
};
var notAGuard = function notAGuard(node) {
return !(node.dataset && node.dataset.focusGuard);
};
var reorderNodes = function reorderNodes(srcNodes, dstNodes) {
return srcNodes.map(function (dnode) {
return dstNodes.find(function (_ref) {
var node = _ref.node;
return dnode === node;
});
}).filter(Boolean);
};
var getFocusMerge = function getFocusMerge(topNode, lastNode) {
var activeElement = document && document.activeElement;
var entries = getAllAffectedNodes(topNode).filter(notAGuard);
var commonParent = getTopCommonParent(activeElement || topNode, topNode, entries);
var innerElements = getTabbableNodes(entries).filter(function (_ref5) {
var node = _ref5.node;
return notAGuard(node);
});
if (!innerElements[0]) {
innerElements = getAllTabbableNodes(entries).filter(function (_ref6) {
var node = _ref6.node;
return notAGuard(node);
});
if (!innerElements[0]) {
return undefined;
}
}
var outerNodes = getTabbableNodes([commonParent]).map(function (_ref7) {
var node = _ref7.node;
return node;
});
var orderedInnerElements = reorderNodes(outerNodes, innerElements);
var innerNodes = orderedInnerElements.map(function (_ref8) {
var node = _ref8.node;
return node;
});
var newId = newFocus(innerNodes, outerNodes, activeElement, lastNode, innerNodes.filter(findAutoFocused(allParentAutofocusables(entries))));
if (newId === undefined) {
return newId;
}
return orderedInnerElements[newId];
};
var focusInFrame = function focusInFrame(frame) {
return frame === document.activeElement;
};
var focusInsideIframe = function focusInsideIframe(topNode) {
return !!arrayFind(toArray(topNode.querySelectorAll('iframe')), focusInFrame);
};
var focusInside = function focusInside(topNode) {
var activeElement = document && document.activeElement;
if (!activeElement || activeElement.dataset && activeElement.dataset.focusGuard) {
return false;
}
return getAllAffectedNodes(topNode).reduce(function (result, node) {
return result || node.contains(activeElement) || focusInsideIframe(node);
}, false);
};
var focusIsHidden = function focusIsHidden() {
return document && toArray(document.querySelectorAll('[' + FOCUS_ALLOW + ']')).some(function (node) {
return node.contains(document.activeElement);
});
};
var focusOn = function focusOn(target) {
target.focus();
if (target.contentWindow) {
target.contentWindow.focus();
}
};
var guardCount = 0;
var lockDisabled = false;
var setFocus = (function (topNode, lastNode) {
var focusable = getFocusMerge(topNode, lastNode);
if (lockDisabled) {
return;
}
if (focusable) {
if (guardCount > 2) {
// eslint-disable-next-line no-console
console.error('FocusLock: focus-fighting detected. Only one focus management system could be active. ' + 'See https://github.com/theKashey/focus-lock/#focus-fighting');
lockDisabled = true;
setTimeout(function () {
lockDisabled = false;
}, 1);
return;
}
guardCount++;
focusOn(focusable.node);
guardCount--;
}
});
var lastActiveTrap = 0;
var lastActiveFocus = null;
var focusOnBody = function focusOnBody() {
return document && document.activeElement === document.body;
};
var isFreeFocus = function isFreeFocus() {
return focusOnBody() || focusIsHidden();
};
var activateTrap = function activateTrap() {
var result = false;
if (lastActiveTrap) {
var observed = lastActiveTrap;
if (!isFreeFocus()) {
if (observed && !focusInside(observed)) {
result = setFocus(observed, lastActiveFocus);
}
lastActiveFocus = document.activeElement;
}
}
return result;
};
var reducePropsToState = function reducePropsToState(propsList) {
return propsList.filter(function (node) {
return node;
}).slice(-1)[0];
};
var handleStateChangeOnClient = function handleStateChangeOnClient(trap) {
lastActiveTrap = trap;
if (trap) {
activateTrap();
}
};
var instances = [];
var emitChange = function emitChange(event) {
if (handleStateChangeOnClient(reducePropsToState(instances))) {
event && event.preventDefault();
return true;
}
return false;
};
var attachHandler = function attachHandler() {
document.addEventListener('focusin', emitChange);
};
var detachHandler = function detachHandler() {
document.removeEventListener('focusin', emitChange);
};
var focusLock = {
on: function on(domNode) {
if (instances.length === 0) {
attachHandler();
}
if (instances.indexOf(domNode) < 0) {
instances.push(domNode);
emitChange();
}
},
off: function off(domNode) {
instances = instances.filter(function (node) {
return node !== domNode;
});
emitChange();
if (instances.length === 0) {
detachHandler();
}
}
};
return focusLock;
}));

View file

@ -2,7 +2,7 @@
"name": "Omni",
"description": "Supercharge Chrome with commands, shortcuts, and more",
"offline_enabled": true,
"version": "1.4.1",
"version": "1.4.2",
"manifest_version": 3,
"action": {
"icons": {
@ -34,7 +34,7 @@
"<all_urls>"
],
"run_at": "document_end",
"js": ["jquery.js", "content.js"],
"js": ["focus.js", "jquery.js", "content.js"],
"css": ["content.css"]
}
],