Skip to content
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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ Website: [https://sergiokas.github.io/Extensity/](https://sergiokas.github.io/Ex

Follow us in Twitter: [@ExtensityChrome](https://twitter.com/ExtensityChrome)

## Keyboard Shortcuts

- **↑/↓ Arrow Keys** - Navigate through extensions
- **Enter** - Open options page for the selected extension
- **Space** - Toggle enable/disable for the selected extension

### What's new:

v1.14.0 (Sep 2024)
Expand Down
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,15 @@ <h1 data-sbind="visible: listedItems.any">Extensions &amp; Apps</h1>

<script type="text/html" id="item-template">
<!-- ko if: !isApp() -->
<li class="extension" data-sbind="click: $parent.toggleExtension, css:{disabled: disabled}"><img width="16px" height="16px" data-sbind="attr:{src:icon}" />
<li class="extension" data-sbind="click: $parent.toggleExtension, css:{disabled: disabled, selected: $index() === $root.selectedIndex()}"><img width="16px" height="16px" data-sbind="attr:{src:icon}" />
<span data-sbind="text: short_name"></span>
<i class="fa fa-flask" title="Development" alt="Development" data-sbind="visible: is_development"></i>
<i class="fa fa-gear" title="Options" alt="Options" data-sbind="visible: $parent.opts.showOptions() && optionsUrl() && !disabled(), click: $parent.launchOptions, clickBubble: false"></i>

</li>
<!-- /ko -->
<!-- ko if: isApp() -->
<li class="app" data-sbind="click: $parent.launchApp">
<li class="app" data-sbind="click: $parent.launchApp, css:{selected: $index() === $root.selectedIndex()}">
<img width="16px" height="16px" data-sbind="attr:{src:icon}"/> <span data-sbind="text: short_name"></span>
<i class="fa fa-flask" title="Development" alt="Development" data-sbind="visible: is_development"></i>
<i class="fa fa-gear" title="Options" alt="Options" data-sbind="visible: $parent.opts.showOptions() && optionsUrl() && !disabled(), click: $parent.launchOptions, clickBubble: false"></i>
Expand Down
83 changes: 83 additions & 0 deletions js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ document.addEventListener("DOMContentLoaded", function() {
self.switch = new SwitchViewModel(self.exts, self.profiles, self.opts);
self.search = new SearchViewModel();
self.activeProfile = ko.observable().extend({persistable: "activeProfile"});
self.selectedIndex = ko.observable(null);

var filterFn = function(i) {
// Filtering function for search box
Expand Down Expand Up @@ -168,12 +169,94 @@ document.addEventListener("DOMContentLoaded", function() {
return (self.dismissals.dismissed("profile_page_viewed") || self.profiles.any());
});

// Helper to scroll selected item into view
var scrollToSelected = function(direction) {
var liElements = document.querySelectorAll('section#content ul li');
if (liElements.length > 0) {
var selectedIndex = 0;
var currentList = self.listedItems();

for (var i = 0; i < self.selectedIndex(); i++) {
if (i < currentList.length) {
selectedIndex++;
}
}

if (selectedIndex < liElements.length) {
var scrollTargetIndex;
if (direction === 'down') {
scrollTargetIndex = Math.min(selectedIndex + 2, liElements.length - 1);
} else {
scrollTargetIndex = Math.max(selectedIndex - 2, 0);
}
if (selectedIndex >= 1) {
liElements[scrollTargetIndex].scrollIntoView({behavior: 'smooth', block: 'nearest'});
} else {
document.querySelector('#header').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
}
}
};

// Keyboard navigation
self.handleKeydown = function(vm, event) {
var key = event.key;
var currentList = self.listedItems();
var selectedIdx = self.selectedIndex();

if (key === 'ArrowDown') {
event.preventDefault();
var nextIndex = (selectedIdx === null) ? 0 : selectedIdx + 1;
if (nextIndex < currentList.length) {
self.selectedIndex(nextIndex);
scrollToSelected('down');
}
} else if (key === 'ArrowUp') {
event.preventDefault();
var prevIndex = (selectedIdx === null) ? currentList.length - 1 : selectedIdx - 1;
if (prevIndex >= 0) {
self.selectedIndex(prevIndex);
scrollToSelected('up');
}
} else if (key === ' ') {
if (selectedIdx !== null) {
event.preventDefault();
var selectedItem = currentList[selectedIdx];
if (selectedItem && !selectedItem.isApp()) {
self.toggleExtension(selectedItem);
}
}
} else if (key === 'Enter') {
if (selectedIdx !== null) {
event.preventDefault();
var selectedItem = currentList[selectedIdx];
if (selectedItem) {
if (selectedItem.isApp()) {
self.launchApp(selectedItem);
} else if (selectedItem.optionsUrl()) {
self.launchOptions(selectedItem);
}
}
}
}
};

// Reset selectedIndex when search changes
self.search.q.subscribe(function() {
self.selectedIndex(null);
});

};

_.defer(function() {
vm = new ExtensityViewModel();
ko.bindingProvider.instance = new ko.secureBindingsProvider({});
ko.applyBindings(vm, document.body);

// Attach keyboard event listener
document.addEventListener('keydown', function(e) {
vm.handleKeydown(vm, e);
});
});

// Workaround for Chrome bug https://bugs.chromium.org/p/chromium/issues/detail?id=307912
Expand Down
17 changes: 17 additions & 0 deletions styles/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,23 @@ section#content li.disabled img {
opacity: 0.3;
}

/** Selected Status (Keyboard Navigation) */
@media (prefers-color-scheme: dark) {
section#content ul li.selected {
background-color: #555;
border-radius: 2px;
box-shadow: 0px 0px 15px rgba(0,0,0,.2) inset;
}
}

@media (prefers-color-scheme: light) {
section#content ul li.selected {
background-color: #d0d0d0;
border-radius: 2px;
box-shadow: 0px 0px 15px rgba(0,0,0,.2) inset;
}
}

section#content li i.fa-gear {
float: right;
margin-top: 2px;
Expand Down