mrfakename commited on
Commit
6e03e6f
·
1 Parent(s): 6356013

Autoplay, keyboard shortcuts

Browse files
Files changed (4) hide show
  1. app/messages.py +3 -0
  2. app/ui.py +103 -2
  3. app/ui_battle.py +37 -7
  4. 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
- with gr.Blocks(css=CSS, theme=gr.themes.Default(font=[gr.themes.GoogleFont("Geist"), "sans-serif"]), title="TTS Arena") as app:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ }""")