Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Commit
·
6e03e6f
1
Parent(s):
6356013
Autoplay, keyboard shortcuts
Browse files- app/messages.py +3 -0
- app/ui.py +103 -2
- app/ui_battle.py +37 -7
- app/ui_vote.py +39 -6
app/messages.py
CHANGED
|
@@ -7,7 +7,10 @@ from .config import *
|
|
| 7 |
MUST_BE_LOGGEDIN = "Please login with Hugging Face to participate in the TTS Arena."
|
| 8 |
DESCR = """
|
| 9 |
# TTS Arena: Benchmarking TTS Models in the Wild
|
|
|
|
| 10 |
Vote to help the community find the best available text-to-speech model!
|
|
|
|
|
|
|
| 11 |
""".strip()
|
| 12 |
BATTLE_INSTR = """
|
| 13 |
## Battle
|
|
|
|
| 7 |
MUST_BE_LOGGEDIN = "Please login with Hugging Face to participate in the TTS Arena."
|
| 8 |
DESCR = """
|
| 9 |
# TTS Arena: Benchmarking TTS Models in the Wild
|
| 10 |
+
|
| 11 |
Vote to help the community find the best available text-to-speech model!
|
| 12 |
+
|
| 13 |
+
Type ? to view keyboard shortcuts.
|
| 14 |
""".strip()
|
| 15 |
BATTLE_INSTR = """
|
| 16 |
## Battle
|
app/ui.py
CHANGED
|
@@ -35,11 +35,112 @@ textbox {resize: none}
|
|
| 35 |
background: var(--body-text-color);
|
| 36 |
}
|
| 37 |
"""
|
| 38 |
-
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
gr.Markdown(DESCR)
|
| 41 |
gr.TabbedInterface([vote, battle, leaderboard, about], ['Vote', 'Battle', 'Leaderboard', 'About'])
|
| 42 |
if CITATION_TEXT:
|
| 43 |
with gr.Row():
|
| 44 |
with gr.Accordion("Citation", open=False):
|
| 45 |
gr.Markdown(f"If you use this data in your publication, please cite us!\n\nCopy the BibTeX citation to cite this source:\n\n```bibtext\n{CITATION_TEXT}\n```\n\nPlease note that all generated audio clips should be assumed unsuitable for redistribution or commercial use.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
background: var(--body-text-color);
|
| 36 |
}
|
| 37 |
"""
|
| 38 |
+
with gr.Blocks(css=CSS + """
|
| 39 |
+
/* Modal styles */
|
| 40 |
+
.shortcuts-modal {
|
| 41 |
+
display: none;
|
| 42 |
+
position: fixed;
|
| 43 |
+
top: 50%;
|
| 44 |
+
left: 50%;
|
| 45 |
+
transform: translate(-50%, -50%);
|
| 46 |
+
background: var(--background-fill-primary);
|
| 47 |
+
padding: 20px;
|
| 48 |
+
border-radius: 8px;
|
| 49 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 50 |
+
z-index: 1000;
|
| 51 |
+
max-width: 500px;
|
| 52 |
+
width: 90%;
|
| 53 |
+
}
|
| 54 |
+
.shortcuts-modal.show {
|
| 55 |
+
display: block;
|
| 56 |
+
}
|
| 57 |
+
.modal-backdrop {
|
| 58 |
+
display: none;
|
| 59 |
+
position: fixed;
|
| 60 |
+
top: 0;
|
| 61 |
+
left: 0;
|
| 62 |
+
width: 100%;
|
| 63 |
+
height: 100%;
|
| 64 |
+
background: rgba(0, 0, 0, 0.5);
|
| 65 |
+
backdrop-filter: blur(10px);
|
| 66 |
+
z-index: 999;
|
| 67 |
+
}
|
| 68 |
+
.modal-backdrop.show {
|
| 69 |
+
display: block;
|
| 70 |
+
}
|
| 71 |
+
.close-button {
|
| 72 |
+
position: absolute;
|
| 73 |
+
top: 10px;
|
| 74 |
+
right: 10px;
|
| 75 |
+
background: none;
|
| 76 |
+
border: none;
|
| 77 |
+
font-size: 20px;
|
| 78 |
+
cursor: pointer;
|
| 79 |
+
color: var(--body-text-color);
|
| 80 |
+
}
|
| 81 |
+
.close-button:hover {
|
| 82 |
+
color: var(--error-text-color);
|
| 83 |
+
}
|
| 84 |
+
""", theme=gr.themes.Default(font=[gr.themes.GoogleFont("Geist"), "sans-serif"]), title="TTS Arena") as app:
|
| 85 |
gr.Markdown(DESCR)
|
| 86 |
gr.TabbedInterface([vote, battle, leaderboard, about], ['Vote', 'Battle', 'Leaderboard', 'About'])
|
| 87 |
if CITATION_TEXT:
|
| 88 |
with gr.Row():
|
| 89 |
with gr.Accordion("Citation", open=False):
|
| 90 |
gr.Markdown(f"If you use this data in your publication, please cite us!\n\nCopy the BibTeX citation to cite this source:\n\n```bibtext\n{CITATION_TEXT}\n```\n\nPlease note that all generated audio clips should be assumed unsuitable for redistribution or commercial use.")
|
| 91 |
+
|
| 92 |
+
# Add modal HTML
|
| 93 |
+
gr.HTML("""
|
| 94 |
+
<div class="modal-backdrop"></div>
|
| 95 |
+
<div class="shortcuts-modal">
|
| 96 |
+
<button class="close-button" onclick="toggleModal(false)">×</button>
|
| 97 |
+
<h3>Keyboard Shortcuts</h3>
|
| 98 |
+
<p><strong>Global:</strong></p>
|
| 99 |
+
<ul>
|
| 100 |
+
<li><code>?</code> or <code>Shift + /</code> - Show this help dialog</li>
|
| 101 |
+
<li><code>Esc</code> - Close this dialog</li>
|
| 102 |
+
</ul>
|
| 103 |
+
<p><strong>Vote & Battle Mode:</strong></p>
|
| 104 |
+
<ul>
|
| 105 |
+
<li><code>r</code> - Generate random text</li>
|
| 106 |
+
<li><code>Ctrl/Cmd + Enter</code> - Synthesize text</li>
|
| 107 |
+
<li><code>a</code> - Vote for option A</li>
|
| 108 |
+
<li><code>b</code> - Vote for option B</li>
|
| 109 |
+
</ul>
|
| 110 |
+
</div>
|
| 111 |
+
""")
|
| 112 |
+
|
| 113 |
+
# Add modal control JavaScript
|
| 114 |
+
app.load(None, None, js="""
|
| 115 |
+
function() {
|
| 116 |
+
function toggleModal(show) {
|
| 117 |
+
document.querySelector('.shortcuts-modal').classList.toggle('show', show);
|
| 118 |
+
document.querySelector('.modal-backdrop').classList.toggle('show', show);
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
document.addEventListener('keydown', function(e) {
|
| 122 |
+
// Only handle shortcuts when not typing in an input
|
| 123 |
+
if (document.activeElement.tagName === 'INPUT' ||
|
| 124 |
+
document.activeElement.tagName === 'TEXTAREA') {
|
| 125 |
+
return;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
// Check for shift + / or ? key
|
| 129 |
+
if ((e.key === '/' && e.shiftKey) || e.key === '?') {
|
| 130 |
+
toggleModal(true);
|
| 131 |
+
}
|
| 132 |
+
// Check for escape key
|
| 133 |
+
else if (e.key === 'Escape') {
|
| 134 |
+
toggleModal(false);
|
| 135 |
+
}
|
| 136 |
+
});
|
| 137 |
+
|
| 138 |
+
// Close modal when clicking backdrop
|
| 139 |
+
document.querySelector('.modal-backdrop').addEventListener('click', function() {
|
| 140 |
+
toggleModal(false);
|
| 141 |
+
});
|
| 142 |
+
|
| 143 |
+
// Make toggleModal available globally
|
| 144 |
+
window.toggleModal = toggleModal;
|
| 145 |
+
}
|
| 146 |
+
""")
|
app/ui_battle.py
CHANGED
|
@@ -20,25 +20,28 @@ with gr.Blocks() as battle:
|
|
| 20 |
with gr.Group():
|
| 21 |
with gr.Row():
|
| 22 |
text = gr.Textbox(container=False, show_label=False, placeholder="Enter text to synthesize", lines=1, max_lines=1, scale=9999999, min_width=0)
|
| 23 |
-
randomt_battle = gr.Button('🎲', scale=0, min_width=0, variant='tool')
|
| 24 |
with gr.Row():
|
| 25 |
with gr.Column(scale=10):
|
| 26 |
model1s = gr.Dropdown(label="Model 1", container=False, show_label=False, choices=AVAILABLE_MODELS.keys(), interactive=True, value=list(AVAILABLE_MODELS.keys())[0])
|
| 27 |
with gr.Column(scale=10):
|
| 28 |
model2s = gr.Dropdown(label="Model 2", container=False, show_label=False, choices=AVAILABLE_MODELS.keys(), interactive=True, value=list(AVAILABLE_MODELS.keys())[1])
|
| 29 |
randomt_battle.click(randomsent_battle, outputs=[text, randomt_battle, model1s, model2s])
|
| 30 |
-
btn = gr.Button("Synthesize", variant='primary')
|
| 31 |
with gr.Row(visible=False) as r2:
|
| 32 |
with gr.Column():
|
| 33 |
with gr.Group():
|
| 34 |
-
aud1 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False)
|
| 35 |
-
abetter = gr.Button("A is better", variant='primary')
|
| 36 |
prevmodel1 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model A", text_align="center", lines=1, max_lines=1, visible=False)
|
| 37 |
with gr.Column():
|
| 38 |
with gr.Group():
|
| 39 |
-
aud2 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False)
|
| 40 |
-
bbetter = gr.Button("B is better", variant='primary')
|
| 41 |
prevmodel2 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model B", text_align="center", lines=1, max_lines=1, visible=False)
|
|
|
|
|
|
|
|
|
|
| 42 |
outputs = [
|
| 43 |
text,
|
| 44 |
btn,
|
|
@@ -56,4 +59,31 @@ with gr.Blocks() as battle:
|
|
| 56 |
nxt_outputs = [abetter, bbetter, prevmodel1, prevmodel2]
|
| 57 |
abetter.click(a_is_better_battle, outputs=nxt_outputs, inputs=[model1, model2, battle_useridstate])
|
| 58 |
bbetter.click(b_is_better_battle, outputs=nxt_outputs, inputs=[model1, model2, battle_useridstate])
|
| 59 |
-
battle.load(random_m, outputs=[model1s, model2s])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
with gr.Group():
|
| 21 |
with gr.Row():
|
| 22 |
text = gr.Textbox(container=False, show_label=False, placeholder="Enter text to synthesize", lines=1, max_lines=1, scale=9999999, min_width=0)
|
| 23 |
+
randomt_battle = gr.Button('🎲', scale=0, min_width=0, variant='tool', elem_id="battle-random-button")
|
| 24 |
with gr.Row():
|
| 25 |
with gr.Column(scale=10):
|
| 26 |
model1s = gr.Dropdown(label="Model 1", container=False, show_label=False, choices=AVAILABLE_MODELS.keys(), interactive=True, value=list(AVAILABLE_MODELS.keys())[0])
|
| 27 |
with gr.Column(scale=10):
|
| 28 |
model2s = gr.Dropdown(label="Model 2", container=False, show_label=False, choices=AVAILABLE_MODELS.keys(), interactive=True, value=list(AVAILABLE_MODELS.keys())[1])
|
| 29 |
randomt_battle.click(randomsent_battle, outputs=[text, randomt_battle, model1s, model2s])
|
| 30 |
+
btn = gr.Button("Synthesize", variant='primary', elem_id="battle-synth-button")
|
| 31 |
with gr.Row(visible=False) as r2:
|
| 32 |
with gr.Column():
|
| 33 |
with gr.Group():
|
| 34 |
+
aud1 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False, autoplay=True, elem_id="battle-a-audio")
|
| 35 |
+
abetter = gr.Button("A is better", variant='primary', elem_id="battle-a-button")
|
| 36 |
prevmodel1 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model A", text_align="center", lines=1, max_lines=1, visible=False)
|
| 37 |
with gr.Column():
|
| 38 |
with gr.Group():
|
| 39 |
+
aud2 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False, elem_id="battle-b-audio")
|
| 40 |
+
bbetter = gr.Button("B is better", variant='primary', elem_id="battle-b-button")
|
| 41 |
prevmodel2 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model B", text_align="center", lines=1, max_lines=1, visible=False)
|
| 42 |
+
aud1.stop(None, None, js="""function() {
|
| 43 |
+
document.getElementById('battle-b-audio')?.querySelector('button.play-pause-button')?.click();
|
| 44 |
+
}""")
|
| 45 |
outputs = [
|
| 46 |
text,
|
| 47 |
btn,
|
|
|
|
| 59 |
nxt_outputs = [abetter, bbetter, prevmodel1, prevmodel2]
|
| 60 |
abetter.click(a_is_better_battle, outputs=nxt_outputs, inputs=[model1, model2, battle_useridstate])
|
| 61 |
bbetter.click(b_is_better_battle, outputs=nxt_outputs, inputs=[model1, model2, battle_useridstate])
|
| 62 |
+
battle.load(random_m, outputs=[model1s, model2s], js="""function() {
|
| 63 |
+
document.addEventListener('keydown', function(e) {
|
| 64 |
+
// Only handle shortcuts if we're on the battle tab
|
| 65 |
+
if (!document.querySelector('#battle-a-button')?.offsetParent) {
|
| 66 |
+
// Check if random button is visible
|
| 67 |
+
if (document.querySelector('#battle-random-button')?.offsetParent) {
|
| 68 |
+
if (e.key === 'r') {
|
| 69 |
+
document.getElementById('battle-random-button')?.click();
|
| 70 |
+
} else if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
| 71 |
+
document.getElementById('battle-synth-button')?.click();
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
return;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
// Only handle A and B keys when not typing in an input
|
| 78 |
+
if (document.activeElement.tagName === 'INPUT' ||
|
| 79 |
+
document.activeElement.tagName === 'TEXTAREA') {
|
| 80 |
+
return;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
if (e.key.toLowerCase() === 'a') {
|
| 84 |
+
document.getElementById('battle-a-button')?.click();
|
| 85 |
+
} else if (e.key.toLowerCase() === 'b') {
|
| 86 |
+
document.getElementById('battle-b-button')?.click();
|
| 87 |
+
}
|
| 88 |
+
});
|
| 89 |
+
}""")
|
app/ui_vote.py
CHANGED
|
@@ -20,9 +20,9 @@ with gr.Blocks() as vote:
|
|
| 20 |
with gr.Group():
|
| 21 |
with gr.Row():
|
| 22 |
text = gr.Textbox(container=False, show_label=False, placeholder="Enter text to synthesize", lines=1, max_lines=1, scale=9999999, min_width=0)
|
| 23 |
-
randomt = gr.Button('🎲', scale=0, min_width=0, variant='tool')
|
| 24 |
randomt.click(randomsent, outputs=[text, randomt])
|
| 25 |
-
btn = gr.Button("Synthesize", variant='primary')
|
| 26 |
model1 = gr.Textbox(interactive=False, lines=1, max_lines=1, visible=False)
|
| 27 |
#model1 = gr.Textbox(interactive=False, lines=1, max_lines=1, visible=True)
|
| 28 |
model2 = gr.Textbox(interactive=False, lines=1, max_lines=1, visible=False)
|
|
@@ -30,14 +30,17 @@ with gr.Blocks() as vote:
|
|
| 30 |
with gr.Row(visible=False) as r2:
|
| 31 |
with gr.Column():
|
| 32 |
with gr.Group():
|
| 33 |
-
aud1 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False)
|
| 34 |
-
abetter = gr.Button("A is better", variant='primary')
|
| 35 |
prevmodel1 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model A", text_align="center", lines=1, max_lines=1, visible=False)
|
| 36 |
with gr.Column():
|
| 37 |
with gr.Group():
|
| 38 |
-
aud2 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False)
|
| 39 |
-
bbetter = gr.Button("B is better", variant='primary')
|
| 40 |
prevmodel2 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model B", text_align="center", lines=1, max_lines=1, visible=False)
|
|
|
|
|
|
|
|
|
|
| 41 |
nxtroundbtn = gr.Button('Next round', visible=False)
|
| 42 |
# outputs = [text, btn, r2, model1, model2, prevmodel1, aud1, prevmodel2, aud2, abetter, bbetter]
|
| 43 |
outputs = [
|
|
@@ -78,3 +81,33 @@ with gr.Blocks() as vote:
|
|
| 78 |
nxt_outputs = [abetter, bbetter, prevmodel1, prevmodel2, nxtroundbtn]
|
| 79 |
abetter.click(a_is_better, outputs=nxt_outputs, inputs=[model1, model2, useridstate])
|
| 80 |
bbetter.click(b_is_better, outputs=nxt_outputs, inputs=[model1, model2, useridstate])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
with gr.Group():
|
| 21 |
with gr.Row():
|
| 22 |
text = gr.Textbox(container=False, show_label=False, placeholder="Enter text to synthesize", lines=1, max_lines=1, scale=9999999, min_width=0)
|
| 23 |
+
randomt = gr.Button('🎲', scale=0, min_width=0, variant='tool', elem_id="vote-random-button")
|
| 24 |
randomt.click(randomsent, outputs=[text, randomt])
|
| 25 |
+
btn = gr.Button("Synthesize", variant='primary', elem_id="vote-synth-button")
|
| 26 |
model1 = gr.Textbox(interactive=False, lines=1, max_lines=1, visible=False)
|
| 27 |
#model1 = gr.Textbox(interactive=False, lines=1, max_lines=1, visible=True)
|
| 28 |
model2 = gr.Textbox(interactive=False, lines=1, max_lines=1, visible=False)
|
|
|
|
| 30 |
with gr.Row(visible=False) as r2:
|
| 31 |
with gr.Column():
|
| 32 |
with gr.Group():
|
| 33 |
+
aud1 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False, autoplay=True, elem_id="vote-a-audio")
|
| 34 |
+
abetter = gr.Button("A is better", variant='primary', elem_id="vote-a-button")
|
| 35 |
prevmodel1 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model A", text_align="center", lines=1, max_lines=1, visible=False)
|
| 36 |
with gr.Column():
|
| 37 |
with gr.Group():
|
| 38 |
+
aud2 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False, elem_id="vote-b-audio")
|
| 39 |
+
bbetter = gr.Button("B is better", variant='primary', elem_id="vote-b-button")
|
| 40 |
prevmodel2 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model B", text_align="center", lines=1, max_lines=1, visible=False)
|
| 41 |
+
aud1.stop(None, None, js="""function() {
|
| 42 |
+
document.getElementById('vote-b-audio')?.querySelector('button.play-pause-button')?.click();
|
| 43 |
+
}""")
|
| 44 |
nxtroundbtn = gr.Button('Next round', visible=False)
|
| 45 |
# outputs = [text, btn, r2, model1, model2, prevmodel1, aud1, prevmodel2, aud2, abetter, bbetter]
|
| 46 |
outputs = [
|
|
|
|
| 81 |
nxt_outputs = [abetter, bbetter, prevmodel1, prevmodel2, nxtroundbtn]
|
| 82 |
abetter.click(a_is_better, outputs=nxt_outputs, inputs=[model1, model2, useridstate])
|
| 83 |
bbetter.click(b_is_better, outputs=nxt_outputs, inputs=[model1, model2, useridstate])
|
| 84 |
+
|
| 85 |
+
# Add custom JS for keyboard shortcuts
|
| 86 |
+
vote.load(None, None, js="""function() {
|
| 87 |
+
document.addEventListener('keydown', function(e) {
|
| 88 |
+
// Only handle shortcuts if we're on the vote tab
|
| 89 |
+
if (!document.querySelector('#vote-a-button')?.offsetParent) {
|
| 90 |
+
// Check if random button is visible
|
| 91 |
+
if (document.querySelector('#vote-random-button')?.offsetParent) {
|
| 92 |
+
if (e.key === 'r') {
|
| 93 |
+
document.getElementById('vote-random-button')?.click();
|
| 94 |
+
} else if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
| 95 |
+
document.getElementById('vote-synth-button')?.click();
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
return;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
// Only handle A and B keys when not typing in an input
|
| 102 |
+
if (document.activeElement.tagName === 'INPUT' ||
|
| 103 |
+
document.activeElement.tagName === 'TEXTAREA') {
|
| 104 |
+
return;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
if (e.key.toLowerCase() === 'a') {
|
| 108 |
+
document.getElementById('vote-a-button')?.click();
|
| 109 |
+
} else if (e.key.toLowerCase() === 'b') {
|
| 110 |
+
document.getElementById('vote-b-button')?.click();
|
| 111 |
+
}
|
| 112 |
+
});
|
| 113 |
+
}""")
|