k-l-lambda commited on
Commit
723e020
·
1 Parent(s): e30c292

refined control styles

Browse files
Files changed (2) hide show
  1. app.py +62 -27
  2. web/score-player.css +1 -1
app.py CHANGED
@@ -203,17 +203,19 @@ def _log_panel (raw=''):
203
  return sys_log
204
 
205
 
206
- def run_generation (prompt, measures, temperature, max_patches, seed, store, top_k=0, top_p=0.9,
207
- progress=gr.Progress()):
208
- '''Streaming generate callback. Yields updates for (log, editor, file_list, store, seed).
209
 
210
  store: {label: lyl_text} dict held in gr.State; the produced document is added
211
  to it under a timestamped label once generation finishes.
212
 
213
  top_k / top_p have fixed defaults (no UI controls); pass them explicitly to override.
214
 
215
- A gr.Progress bar tracks generation: by completed measures when a measure count
216
- is requested, else by generated patches out of max_patches.
 
 
 
217
 
218
  The output file is named with the seed used for THIS generation; on completion
219
  the seed slider is randomized (final yield) so the next click uses a fresh seed.
@@ -227,7 +229,6 @@ def run_generation (prompt, measures, temperature, max_patches, seed, store, top
227
  n_yields = 0
228
  mp = int(max_patches)
229
  t0 = time.perf_counter()
230
- progress(0.0, desc='Starting…')
231
  try:
232
  gen = get_generator()
233
  # drop a marker in the log timeline; _log_panel expands it to the live output,
@@ -238,25 +239,22 @@ def run_generation (prompt, measures, temperature, max_patches, seed, store, top
238
  top_k=int(top_k), top_p=float(top_p), measures=meas, seed=int(seed)):
239
  if not done:
240
  n_yields += 1
241
- # progress: by measures (count completed `|` separators vs target) when a
242
- # measure count was requested, else by patch budget. clamp to [0,1).
243
  if meas:
244
- frac = raw.count('|') / meas
245
- desc = 'measure %d/%d' % (min(raw.count('|'), meas), meas)
246
  else:
247
- frac = max(0, n_yields - 1) / mp
248
- desc = 'patch %d/%d' % (max(0, n_yields - 1), mp)
249
- progress(min(frac, 0.99) if not done else 1.0, desc=desc)
250
  # The log streams every patch (raw text). The editor, however, must stay
251
  # syntactically valid: only sync it at a measure boundary — i.e. when the
252
  # accumulated text ends with the measure separator `|` (so it never shows a
253
  # half-generated, incomplete measure). `done` forces a final sync.
254
  at_boundary = raw.rstrip().endswith('|')
255
  editor_update = pretty if (at_boundary or done) else gr.update()
256
- yield _log_panel(raw), editor_update, gr.update(), store, gr.update()
257
  except Exception as e:
258
  LOG.exception('generation failed: %s', e)
259
- yield _log_panel(raw), pretty, gr.update(), store, gr.update()
260
  return
261
 
262
  # timing: the stream yields once for prefill + once per generated patch, so the
@@ -278,7 +276,7 @@ def run_generation (prompt, measures, temperature, max_patches, seed, store, top
278
  # randomize the seed slider for the next run (the file above already used the
279
  # seed this generation ran with, so naming is unaffected)
280
  next_seed = random.randint(0, 2147483647)
281
- yield _log_panel(raw), pretty, gr.update(choices=list(store.keys()), value=label), store, gr.update(value=next_seed)
282
 
283
 
284
  def load_file (label, store):
@@ -361,11 +359,25 @@ function (text) {
361
  # must return its args unchanged — returning nothing makes Gradio send null for
362
  # every input (which breaks e.g. the temperature Slider's preprocessor).
363
  _JS_GEN_START = '''
364
- function (...args) { if (window.LilyScore) window.LilyScore.setGenerating(true); return args; }
 
 
 
 
 
 
 
 
365
  '''
366
 
367
  _JS_GEN_END = '''
368
- function () { if (window.LilyScore) window.LilyScore.setGenerating(false); }
 
 
 
 
 
 
369
  '''
370
 
371
 
@@ -390,6 +402,25 @@ CUSTOM_CSS = '''
390
  max-height: 324px;
391
  overflow-y: auto;
392
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
  '''
394
 
395
 
@@ -423,8 +454,8 @@ def build_ui ():
423
  max_patches = gr.Number(label='max patches', value=1024, precision=0)
424
  seed = gr.Slider(0, 2147483647, value=42, step=1, label='seed')
425
  with gr.Row():
426
- gen_btn = gr.Button('Generate', variant='primary')
427
- stop_btn = gr.Button('Stop', variant='stop')
428
 
429
  with gr.Accordion('Logs', open=True):
430
  log = gr.Textbox(show_label=False, lines=10, max_lines=10,
@@ -465,12 +496,12 @@ def build_ui ():
465
  gen_event = gen_btn.click(
466
  run_generation,
467
  inputs=[prompt, measures, temperature, max_patches, seed, store],
468
- outputs=[log, editor, file_list, store, seed],
469
  js=_JS_GEN_START,
470
- # "minimal" = a small runtime/progress indicator only (top-right), NOT a
471
- # spinner covering the output area. "full" overlays the Logs/editor
472
- # outputs and, since we yield every patch, flickers badly.
473
- show_progress='minimal',
474
  )
475
  gen_event.then(None, None, None, js=_JS_GEN_END)
476
 
@@ -479,8 +510,12 @@ def build_ui ():
479
  # virtualises long docs in the DOM, so scraping it client-side truncates).
480
  editor.change(None, inputs=[editor], outputs=None, js=_JS_RENDER)
481
 
482
- # Stop: cancel generation, then lift the gate so the player returns
483
- stop_btn.click(None, None, None, cancels=[gen_event]).then(None, None, None, js=_JS_GEN_END)
 
 
 
 
484
  file_list.select(load_file, inputs=[file_list, store], outputs=[editor])
485
 
486
  return demo
 
203
  return sys_log
204
 
205
 
206
+ def run_generation (prompt, measures, temperature, max_patches, seed, store, top_k=0, top_p=0.9):
207
+ '''Streaming generate callback. Yields updates for (log, editor, file_list, store, seed, gen_btn).
 
208
 
209
  store: {label: lyl_text} dict held in gr.State; the produced document is added
210
  to it under a timestamped label once generation finishes.
211
 
212
  top_k / top_p have fixed defaults (no UI controls); pass them explicitly to override.
213
 
214
+ Progress is shown on the Generate button itself: its label becomes
215
+ "Generating… M/N" (by completed measures when a measure count is requested, else
216
+ by patches out of max_patches) during the run, and reverts to "Generate" at the
217
+ end. (Gradio's native progress bar can't render on a Button, so we drive the
218
+ label directly.)
219
 
220
  The output file is named with the seed used for THIS generation; on completion
221
  the seed slider is randomized (final yield) so the next click uses a fresh seed.
 
229
  n_yields = 0
230
  mp = int(max_patches)
231
  t0 = time.perf_counter()
 
232
  try:
233
  gen = get_generator()
234
  # drop a marker in the log timeline; _log_panel expands it to the live output,
 
239
  top_k=int(top_k), top_p=float(top_p), measures=meas, seed=int(seed)):
240
  if not done:
241
  n_yields += 1
242
+ # progress on the Generate button label: by measures (completed `|`
243
+ # separators vs target) when a measure count was requested, else by patches.
244
  if meas:
245
+ btn_label = 'Generating… %d/%d' % (min(raw.count('|'), meas), meas)
 
246
  else:
247
+ btn_label = 'Generating… %d/%d' % (max(0, n_yields - 1), mp)
 
 
248
  # The log streams every patch (raw text). The editor, however, must stay
249
  # syntactically valid: only sync it at a measure boundary — i.e. when the
250
  # accumulated text ends with the measure separator `|` (so it never shows a
251
  # half-generated, incomplete measure). `done` forces a final sync.
252
  at_boundary = raw.rstrip().endswith('|')
253
  editor_update = pretty if (at_boundary or done) else gr.update()
254
+ yield _log_panel(raw), editor_update, gr.update(), store, gr.update(), gr.update(value=btn_label)
255
  except Exception as e:
256
  LOG.exception('generation failed: %s', e)
257
+ yield _log_panel(raw), pretty, gr.update(), store, gr.update(), gr.update(value='Generate')
258
  return
259
 
260
  # timing: the stream yields once for prefill + once per generated patch, so the
 
276
  # randomize the seed slider for the next run (the file above already used the
277
  # seed this generation ran with, so naming is unaffected)
278
  next_seed = random.randint(0, 2147483647)
279
+ yield _log_panel(raw), pretty, gr.update(choices=list(store.keys()), value=label), store, gr.update(value=next_seed), gr.update(value='Generate')
280
 
281
 
282
  def load_file (label, store):
 
359
  # must return its args unchanged — returning nothing makes Gradio send null for
360
  # every input (which breaks e.g. the temperature Slider's preprocessor).
361
  _JS_GEN_START = '''
362
+ function (...args) {
363
+ if (window.LilyScore) window.LilyScore.setGenerating(true);
364
+ // turn Generate yellow + Stop red while running
365
+ ['gen-btn', 'stop-btn'].forEach(function (idv) {
366
+ const b = document.getElementById(idv);
367
+ if (b) b.classList.add('ls-generating');
368
+ });
369
+ return args;
370
+ }
371
  '''
372
 
373
  _JS_GEN_END = '''
374
+ function () {
375
+ if (window.LilyScore) window.LilyScore.setGenerating(false);
376
+ ['gen-btn', 'stop-btn'].forEach(function (idv) {
377
+ const b = document.getElementById(idv);
378
+ if (b) b.classList.remove('ls-generating');
379
+ });
380
+ }
381
  '''
382
 
383
 
 
402
  max-height: 324px;
403
  overflow-y: auto;
404
  }
405
+ /* Generate button turns yellow while a generation is running (the .ls-generating
406
+ class is toggled by the generation-gate js). !important beats the primary variant. */
407
+ #gen-btn.ls-generating {
408
+ background: #f5c518 !important;
409
+ background-image: none !important;
410
+ border-color: #d4a800 !important;
411
+ color: #3a2f00 !important;
412
+ }
413
+ /* Stop button: grey by default (theme look), solid red only while generating
414
+ (the .ls-generating class is toggled by the generation-gate js). */
415
+ #stop-btn.ls-generating {
416
+ background: #e23b3b !important;
417
+ background-image: none !important;
418
+ border-color: #c42b2b !important;
419
+ color: #fff !important;
420
+ }
421
+ #stop-btn.ls-generating:hover {
422
+ background: #cf2e2e !important;
423
+ }
424
  '''
425
 
426
 
 
454
  max_patches = gr.Number(label='max patches', value=1024, precision=0)
455
  seed = gr.Slider(0, 2147483647, value=42, step=1, label='seed')
456
  with gr.Row():
457
+ gen_btn = gr.Button('Generate', variant='primary', elem_id='gen-btn')
458
+ stop_btn = gr.Button('Stop', variant='stop', elem_id='stop-btn')
459
 
460
  with gr.Accordion('Logs', open=True):
461
  log = gr.Textbox(show_label=False, lines=10, max_lines=10,
 
496
  gen_event = gen_btn.click(
497
  run_generation,
498
  inputs=[prompt, measures, temperature, max_patches, seed, store],
499
+ outputs=[log, editor, file_list, store, seed, gen_btn],
500
  js=_JS_GEN_START,
501
+ # progress is shown on the Generate button's own label ("Generating… M/N"),
502
+ # driven by run_generation. Hide Gradio's native progress overlay (it can't
503
+ # render on a Button and otherwise covers the Logs/editor outputs).
504
+ show_progress='hidden',
505
  )
506
  gen_event.then(None, None, None, js=_JS_GEN_END)
507
 
 
510
  # virtualises long docs in the DOM, so scraping it client-side truncates).
511
  editor.change(None, inputs=[editor], outputs=None, js=_JS_RENDER)
512
 
513
+ # Stop: cancel generation, reset the Generate button label (the cancelled
514
+ # run never reaches its final yield, so it'd otherwise stay "Generating…"),
515
+ # then lift the gate (js) so the player returns + button colors revert.
516
+ stop_btn.click(
517
+ lambda: gr.update(value='Generate'), None, outputs=[gen_btn], cancels=[gen_event],
518
+ ).then(None, None, None, js=_JS_GEN_END)
519
  file_list.select(load_file, inputs=[file_list, store], outputs=[editor])
520
 
521
  return demo
web/score-player.css CHANGED
@@ -27,7 +27,7 @@
27
  .ls-status.ls-err { color: #c0392b; }
28
 
29
  .ls-svg {
30
- background: #fff;
31
  display: inline-block;
32
  min-width: 100%;
33
  }
 
27
  .ls-status.ls-err { color: #c0392b; }
28
 
29
  .ls-svg {
30
+ background: #fffdf2; /* faint warm/yellow tint behind the score */
31
  display: inline-block;
32
  min-width: 100%;
33
  }