keyboard shortcuts

This commit is contained in:
rimu 2024-01-21 12:14:05 +13:00
parent 16eb228052
commit 66f621049a
6 changed files with 159 additions and 5 deletions

View file

@ -215,6 +215,11 @@ def robots():
return resp
@bp.route('/keyboard_shortcuts')
def keyboard_shortcuts():
return render_template('keyboard_shortcuts.html')
@bp.route('/test')
def test():
return pytesseract.image_to_string(Image.open('test.png').convert('L'))

View file

@ -361,13 +361,17 @@ function setupTimeTracking() {
}
}
var currentPost;
var currentPost; // keep track of which is the current post. Set by mouse movements (see const votableElements) and by J and K key presses
var showCurrentPost = false; // when true, the currently selected post will be visibly different from the others. Set to true by J and K key presses
function setupKeyboardShortcuts() {
document.addEventListener('keydown', function(event) {
if (document.activeElement.tagName !== 'INPUT' && document.activeElement.tagName !== 'TEXTAREA') {
var didSomething = false;
if (event.key === 'a') {
if(event.shiftKey && event.key === '?') {
location.href = '/keyboard_shortcuts';
didSomething = true;
} else if (event.key === 'a') {
if(currentPost) {
currentPost.querySelector('.upvote_button').click();
didSomething = true;
@ -392,6 +396,48 @@ function setupKeyboardShortcuts() {
currentPost.querySelector('.post_teaser_title_a').click();
didSomething = true;
}
} else if (event.key === 'j') {
showCurrentPost = true;
if(currentPost) {
if(currentPost.nextElementSibling) {
var elementToRemoveClass = document.querySelector('.post_teaser.current_post');
if(elementToRemoveClass)
elementToRemoveClass.classList.remove('current_post');
currentPost = currentPost.nextElementSibling;
currentPost.classList.add('current_post');
}
didSomething = true;
}
else {
currentPost = document.querySelector('.post_teaser');
currentPost.classList.add('current_post');
}
// Check if the current post is out of the viewport
var rect = currentPost.getBoundingClientRect();
if (rect.bottom > window.innerHeight || rect.top < 0) {
currentPost.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
} else if (event.key === 'k') {
showCurrentPost = true;
if(currentPost) {
if(currentPost.previousElementSibling) {
var elementToRemoveClass = document.querySelector('.post_teaser.current_post');
if(elementToRemoveClass)
elementToRemoveClass.classList.remove('current_post');
currentPost = currentPost.previousElementSibling;
currentPost.classList.add('current_post');
}
didSomething = true;
}
else {
currentPost = document.querySelector('.post_teaser');
currentPost.classList.add('current_post');
}
// Check if the current post is out of the viewport
var rect = currentPost.getBoundingClientRect();
if (rect.bottom > window.innerHeight || rect.top < 0) {
currentPost.scrollIntoView({ behavior: 'smooth', block: 'end' });
}
}
if(didSomething) {
event.preventDefault();
@ -399,13 +445,22 @@ function setupKeyboardShortcuts() {
}
});
const votableElements = document.querySelectorAll('.post_teaser, .comments .comment, .post_type_image, .post_type_normal');
const votableElements = document.querySelectorAll('.post_teaser, .post_type_image, .post_type_normal');
votableElements.forEach(votable => {
votable.addEventListener('mouseover', event => {
currentPost = event.currentTarget;
if(showCurrentPost) {
var elementToRemoveClass = document.querySelector('.post_teaser.current_post');
elementToRemoveClass.classList.remove('current_post');
currentPost.classList.add('current_post');
}
});
votable.addEventListener('mouseout', event => {
currentPost = null;
//currentPost = null;
if(showCurrentPost) {
//var elementToRemoveClass = document.querySelector('.post_teaser.current_post');
//elementToRemoveClass.classList.remove('current_post');
}
});
});
}

View file

@ -642,6 +642,17 @@ nav.navbar {
color: black;
}
.current_post {
background-color: aliceblue;
}
.current_post .replies {
background-color: var(--bs-body-bg);
}
[data-bs-theme=dark] .current_post {
background-color: #424549;
}
@media (min-width: 992px) {
.h1, h1 {
font-size: 2rem;

View file

@ -266,6 +266,17 @@ nav.navbar {
color: black;
}
.current_post {
background-color: aliceblue;
.replies {
background-color: var(--bs-body-bg);
}
}
[data-bs-theme=dark] .current_post {
background-color: $super-dark-grey;
}
.h1, h1 {
@include breakpoint(tablet) {
font-size: 2rem;

View file

@ -0,0 +1,68 @@
{% extends "base.html" %}
{% block app_content %}
<div class="row">
<div class="col-12 col-md-8 position-relative main_pane">
<h1>{{ _('Keyboard shortcuts') }}</h1>
<p>{{ _('Most shortcuts are the same as what reddit has.') }}</p>
<div class="row">
<div class="col-12 col-md-6">
<h3>{{ _('Navigation') }}</h3>
<table class="table table-striped">
<tr>
<td>Show shortcut help</td>
<td>Shift + ?</td>
</tr>
<tr>
<td>Next post</td>
<td>J</td>
</tr>
<tr>
<td>Previous post</td>
<td>K</td>
</tr>
<tr>
<td>Open post</td>
<td>Enter</td>
</tr>
<tr>
<td>Open/close preview</td>
<td>X</td>
</tr>
<tr>
<td>Go to post link</td>
<td>L</td>
</tr>
</table>
</div>
<div class="col-12 col-md-6">
<h3>{{ _('Action') }}</h3>
<table class="table table-striped">
<tr>
<td>{{ _('Upvote') }}</td>
<td>A</td>
</tr>
<tr>
<td>{{ _('Downvote') }}</td>
<td>Z</td>
</tr>
</table>
</div>
<p>{{ _('When viewing a list of posts actions like voting or going to a post depend on which is the current post. The current post is determined by hovering with the mouse or the J and K keys.') }}</p>
</div>
</div>
<div class="col-12 col-md-4 side_pane">
<div class="card mt-3">
<div class="card-header">
<h2>{{ _('About %(site_name)s', site_name=g.site.name) }}</h2>
</div>
<div class="card-body">
<p><strong>{{ g.site.description|safe }}</strong></p>
<p>{{ g.site.sidebar|safe }}</p>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -201,5 +201,9 @@
{% include "_inoculation_links.html" %}
</div>
</div>
<script nonce="{{ session['nonce'] }}">
window.addEventListener("load", function () {
currentPost = document.querySelector('.post_col'); // set the current post, so A and Z voting keys work when viewing a post page
});
</script>
{% endblock %}