k-l-lambda commited on
Commit
43d70fc
·
1 Parent(s): 2a7be55

added share link.

Browse files
Files changed (2) hide show
  1. app.py +69 -0
  2. web/lyl-editor.css +24 -0
app.py CHANGED
@@ -450,6 +450,66 @@ function (value) {
450
  }
451
  '''
452
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
453
  # Render the editor text to SVG. The text is passed in by Gradio as the event's
454
  # input value (the hidden #ls-editor-state textbox, kept in sync with the embedded
455
  # CodeMirror editor) — taking the value as an argument gives the full text directly,
@@ -687,6 +747,12 @@ def build_ui ():
687
  with gr.Column(scale=5, elem_id='editor-col'):
688
  with gr.Group():
689
  gr.Markdown('## Lilylet editor')
 
 
 
 
 
 
690
  # Our own CodeMirror 6 editor (lyl-editor.bundle.js) mounts into
691
  # this div and bridges to the hidden textbox below — Gradio's
692
  # gr.Code can't be syntax-highlighted (its CM is sealed), so we
@@ -744,6 +810,9 @@ def build_ui ():
744
  file_list.select(load_file, inputs=[file_list, store], outputs=[editor])
745
  # separate js-only listener: mirror the selected file into location.hash for deep-linking
746
  file_list.select(None, inputs=[file_list], outputs=None, js=_JS_SELECT_HASH)
 
 
 
747
 
748
  return demo
749
 
 
450
  }
451
  '''
452
 
453
+ # Share button: copy the current deep-link URL to the clipboard and flash a hint.
454
+ #
455
+ # iframe notes (this app is embedded in a cross-origin iframe on HF Spaces):
456
+ # - location.href ALWAYS reads THIS frame's own document URL — the Gradio app's
457
+ # direct URL incl. the #score=<file> hash — even inside a cross-origin iframe.
458
+ # The same-origin policy only blocks reading window.parent/window.top.location,
459
+ # not your own frame's. So this is exactly the shareable deep-link; the outer
460
+ # huggingface.co/spaces URL is both unreadable AND wrong (it has no hash).
461
+ # - navigator.clipboard.writeText may be blocked by Permissions-Policy in a
462
+ # cross-origin iframe (needs allow="clipboard-write", which the HF embed may not
463
+ # grant). So we try the async Clipboard API first and fall back to a hidden
464
+ # <textarea> + document.execCommand('copy'), which works from a user gesture.
465
+ _JS_SHARE = '''
466
+ function () {
467
+ const url = location.href;
468
+ const flash = (msg, ok) => {
469
+ const btn = document.getElementById('ls-share-btn');
470
+ if (!btn) return;
471
+ let hint = document.getElementById('ls-share-hint');
472
+ if (!hint) {
473
+ hint = document.createElement('span');
474
+ hint.id = 'ls-share-hint';
475
+ hint.className = 'ls-share-hint';
476
+ btn.insertAdjacentElement('afterend', hint);
477
+ }
478
+ hint.textContent = msg;
479
+ hint.classList.toggle('ls-share-err', !ok);
480
+ hint.classList.add('show');
481
+ clearTimeout(hint._t);
482
+ hint._t = setTimeout(() => hint.classList.remove('show'), 2200);
483
+ };
484
+ // No score selected yet -> no #score= hash; the bare URL isn't a useful share link.
485
+ if (!/(?:^#|&|#)score=/.test(location.hash || '')) {
486
+ flash('Select a score first', false);
487
+ return [];
488
+ }
489
+ const fallback = () => {
490
+ try {
491
+ const ta = document.createElement('textarea');
492
+ ta.value = url; ta.style.position = 'fixed'; ta.style.opacity = '0';
493
+ document.body.appendChild(ta); ta.focus(); ta.select();
494
+ const ok = document.execCommand('copy');
495
+ document.body.removeChild(ta);
496
+ flash(ok ? 'Link copied!' : 'Copy failed — select & copy manually', ok);
497
+ } catch (e) {
498
+ flash('Copy failed — select & copy manually', false);
499
+ }
500
+ };
501
+ try {
502
+ if (navigator.clipboard && navigator.clipboard.writeText) {
503
+ navigator.clipboard.writeText(url).then(
504
+ () => flash('Link copied!', true),
505
+ () => fallback()
506
+ );
507
+ } else { fallback(); }
508
+ } catch (e) { fallback(); }
509
+ return [];
510
+ }
511
+ '''
512
+
513
  # Render the editor text to SVG. The text is passed in by Gradio as the event's
514
  # input value (the hidden #ls-editor-state textbox, kept in sync with the embedded
515
  # CodeMirror editor) — taking the value as an argument gives the full text directly,
 
747
  with gr.Column(scale=5, elem_id='editor-col'):
748
  with gr.Group():
749
  gr.Markdown('## Lilylet editor')
750
+ # Share button: copies the current deep-link URL (#score=<file>)
751
+ # to the clipboard. js-only handler (_JS_SHARE) — see its comment
752
+ # for the iframe URL/clipboard caveats. A transient hint span next
753
+ # to it shows the copy result.
754
+ share_btn = gr.Button('🔗 Share link', elem_id='ls-share-btn',
755
+ size='sm', scale=0, min_width=110)
756
  # Our own CodeMirror 6 editor (lyl-editor.bundle.js) mounts into
757
  # this div and bridges to the hidden textbox below — Gradio's
758
  # gr.Code can't be syntax-highlighted (its CM is sealed), so we
 
810
  file_list.select(load_file, inputs=[file_list, store], outputs=[editor])
811
  # separate js-only listener: mirror the selected file into location.hash for deep-linking
812
  file_list.select(None, inputs=[file_list], outputs=None, js=_JS_SELECT_HASH)
813
+ # Share: copy the current deep-link URL (the #score hash kept current by the
814
+ # select listener above) to the clipboard. js-only — see _JS_SHARE.
815
+ share_btn.click(None, inputs=None, outputs=None, js=_JS_SHARE)
816
 
817
  return demo
818
 
web/lyl-editor.css CHANGED
@@ -30,3 +30,27 @@
30
  #ls-editor-mount .cm-editor.cm-focused {
31
  outline: none;
32
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  #ls-editor-mount .cm-editor.cm-focused {
31
  outline: none;
32
  }
33
+
34
+ /* Share button: a compact, unobtrusive button under the "Lilylet editor" title. */
35
+ #ls-share-btn {
36
+ flex: 0 0 auto;
37
+ width: auto;
38
+ min-width: 0;
39
+ align-self: flex-start;
40
+ margin: 0 0 6px 2px;
41
+ font-size: 12px;
42
+ padding: 3px 10px;
43
+ }
44
+
45
+ /* Transient "Link copied!" hint shown beside the share button. Fades in on .show. */
46
+ .ls-share-hint {
47
+ display: inline-block;
48
+ margin-left: 8px;
49
+ font-size: 12px;
50
+ color: #2b7;
51
+ opacity: 0;
52
+ transition: opacity 0.2s ease;
53
+ vertical-align: middle;
54
+ }
55
+ .ls-share-hint.show { opacity: 1; }
56
+ .ls-share-hint.ls-share-err { color: #c0392b; }