Spaces:
Running
Running
add preview for transformers js
Browse files
app.py
CHANGED
|
@@ -805,6 +805,96 @@ def format_transformers_js_output(files):
|
|
| 805 |
output.append(files['style.css'])
|
| 806 |
return '\n'.join(output)
|
| 807 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 808 |
def parse_svelte_output(text):
|
| 809 |
"""Parse Svelte output to extract individual files"""
|
| 810 |
files = {
|
|
@@ -2218,7 +2308,7 @@ This will help me create a better design for you."""
|
|
| 2218 |
yield {
|
| 2219 |
code_output: formatted_output,
|
| 2220 |
history: _history,
|
| 2221 |
-
sandbox:
|
| 2222 |
history_output: history_to_chatbot_messages(_history),
|
| 2223 |
}
|
| 2224 |
else:
|
|
@@ -2462,16 +2552,17 @@ This will help me create a better design for you."""
|
|
| 2462 |
yield {
|
| 2463 |
code_output: gr.update(value=formatted_output, language="html"),
|
| 2464 |
history_output: history_to_chatbot_messages(_history),
|
| 2465 |
-
sandbox:
|
| 2466 |
}
|
| 2467 |
elif has_existing_content:
|
| 2468 |
# Model is returning search/replace changes for transformers.js - apply them
|
| 2469 |
last_content = _history[-1][1] if _history and len(_history[-1]) > 1 else ""
|
| 2470 |
modified_content = apply_transformers_js_search_replace_changes(last_content, content)
|
|
|
|
| 2471 |
yield {
|
| 2472 |
code_output: gr.update(value=modified_content, language="html"),
|
| 2473 |
history_output: history_to_chatbot_messages(_history),
|
| 2474 |
-
sandbox:
|
| 2475 |
}
|
| 2476 |
else:
|
| 2477 |
# Still streaming, show partial content
|
|
@@ -2549,7 +2640,7 @@ This will help me create a better design for you."""
|
|
| 2549 |
yield {
|
| 2550 |
code_output: formatted_output,
|
| 2551 |
history: _history,
|
| 2552 |
-
sandbox:
|
| 2553 |
history_output: history_to_chatbot_messages(_history),
|
| 2554 |
}
|
| 2555 |
elif has_existing_content:
|
|
@@ -2557,10 +2648,11 @@ This will help me create a better design for you."""
|
|
| 2557 |
last_content = _history[-1][1] if _history and len(_history[-1]) > 1 else ""
|
| 2558 |
modified_content = apply_transformers_js_search_replace_changes(last_content, content)
|
| 2559 |
_history.append([query, modified_content])
|
|
|
|
| 2560 |
yield {
|
| 2561 |
code_output: modified_content,
|
| 2562 |
history: _history,
|
| 2563 |
-
sandbox:
|
| 2564 |
history_output: history_to_chatbot_messages(_history),
|
| 2565 |
}
|
| 2566 |
else:
|
|
@@ -3650,7 +3742,7 @@ with gr.Blocks(
|
|
| 3650 |
if language == "transformers.js":
|
| 3651 |
files = parse_transformers_js_output(code)
|
| 3652 |
if files['index.html']:
|
| 3653 |
-
return
|
| 3654 |
return "<div style='padding:1em;color:#888;text-align:center;'>Preview is only available for HTML. Please download your code using the download button above.</div>"
|
| 3655 |
if language == "svelte":
|
| 3656 |
return "<div style='padding:1em;color:#888;text-align:center;'>Preview is only available for HTML. Please download your Svelte code and deploy it to see the result.</div>"
|
|
|
|
| 805 |
output.append(files['style.css'])
|
| 806 |
return '\n'.join(output)
|
| 807 |
|
| 808 |
+
def build_transformers_inline_html(files: dict) -> str:
|
| 809 |
+
"""Merge transformers.js three-file output into a single self-contained HTML document.
|
| 810 |
+
|
| 811 |
+
- Inlines style.css into a <style> tag
|
| 812 |
+
- Inlines index.js into a <script type="module"> tag
|
| 813 |
+
- Rewrites ESM imports for transformers.js to a stable CDN URL so it works in data: iframes
|
| 814 |
+
"""
|
| 815 |
+
import re as _re
|
| 816 |
+
|
| 817 |
+
html = files.get('index.html') or ''
|
| 818 |
+
js = files.get('index.js') or ''
|
| 819 |
+
css = files.get('style.css') or ''
|
| 820 |
+
|
| 821 |
+
# Normalize JS imports to CDN (handle both @huggingface/transformers and legacy @xenova/transformers)
|
| 822 |
+
cdn_url = "https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.7.1"
|
| 823 |
+
js = _re.sub(r"from\s+['\"]@huggingface/transformers['\"]", f"from '{cdn_url}'", js)
|
| 824 |
+
js = _re.sub(r"from\s+['\"]@xenova/transformers['\"]", f"from '{cdn_url}'", js)
|
| 825 |
+
|
| 826 |
+
# Prepend a small prelude to reduce persistent caching during preview
|
| 827 |
+
# Note: importing env alongside user's own imports is fine in ESM
|
| 828 |
+
if js.strip():
|
| 829 |
+
prelude = (
|
| 830 |
+
f"import {{ env }} from '{cdn_url}';\n"
|
| 831 |
+
"try { env.useBrowserCache = false; } catch (e) {}\n"
|
| 832 |
+
)
|
| 833 |
+
js = prelude + js
|
| 834 |
+
|
| 835 |
+
# If index.html missing or doesn't look like a full document, create a minimal shell
|
| 836 |
+
doc = html.strip()
|
| 837 |
+
if not doc or ('<html' not in doc.lower()):
|
| 838 |
+
doc = (
|
| 839 |
+
"<!DOCTYPE html>\n"
|
| 840 |
+
"<html>\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Transformers.js App</title>\n</head>\n"
|
| 841 |
+
"<body>\n<div id=\"app\"></div>\n</body>\n</html>"
|
| 842 |
+
)
|
| 843 |
+
|
| 844 |
+
# Remove local references to style.css and index.js to avoid duplicates when inlining
|
| 845 |
+
doc = _re.sub(r"<link[^>]+href=\"[^\"]*style\.css\"[^>]*>\s*", "", doc, flags=_re.IGNORECASE)
|
| 846 |
+
doc = _re.sub(r"<script[^>]+src=\"[^\"]*index\.js\"[^>]*>\s*</script>\s*", "", doc, flags=_re.IGNORECASE)
|
| 847 |
+
|
| 848 |
+
# Inline CSS: insert before </head> or create a <head>
|
| 849 |
+
style_tag = f"<style>\n{css}\n</style>" if css else ""
|
| 850 |
+
if style_tag:
|
| 851 |
+
if '</head>' in doc.lower():
|
| 852 |
+
# Preserve original casing by finding closing head case-insensitively
|
| 853 |
+
match = _re.search(r"</head>", doc, flags=_re.IGNORECASE)
|
| 854 |
+
if match:
|
| 855 |
+
idx = match.start()
|
| 856 |
+
doc = doc[:idx] + style_tag + doc[idx:]
|
| 857 |
+
else:
|
| 858 |
+
# No head; insert at top of body
|
| 859 |
+
match = _re.search(r"<body[^>]*>", doc, flags=_re.IGNORECASE)
|
| 860 |
+
if match:
|
| 861 |
+
idx = match.end()
|
| 862 |
+
doc = doc[:idx] + "\n" + style_tag + doc[idx:]
|
| 863 |
+
else:
|
| 864 |
+
# Append at beginning
|
| 865 |
+
doc = style_tag + doc
|
| 866 |
+
|
| 867 |
+
# Inline JS: insert before </body>
|
| 868 |
+
script_tag = f"<script type=\"module\">\n{js}\n</script>" if js else ""
|
| 869 |
+
# Cleanup script to clear Cache Storage and IndexedDB on unload to free model weights
|
| 870 |
+
cleanup_tag = (
|
| 871 |
+
"<script>\n"
|
| 872 |
+
"(function(){\n"
|
| 873 |
+
" function cleanup(){\n"
|
| 874 |
+
" try { if (window.caches && caches.keys) { caches.keys().then(keys => keys.forEach(k => caches.delete(k))); } } catch(e){}\n"
|
| 875 |
+
" try { if (window.indexedDB && indexedDB.databases) { indexedDB.databases().then(dbs => dbs.forEach(db => db && db.name && indexedDB.deleteDatabase(db.name))); } } catch(e){}\n"
|
| 876 |
+
" }\n"
|
| 877 |
+
" window.addEventListener('pagehide', cleanup, { once: true });\n"
|
| 878 |
+
" window.addEventListener('beforeunload', cleanup, { once: true });\n"
|
| 879 |
+
"})();\n"
|
| 880 |
+
"</script>"
|
| 881 |
+
)
|
| 882 |
+
if script_tag:
|
| 883 |
+
match = _re.search(r"</body>", doc, flags=_re.IGNORECASE)
|
| 884 |
+
if match:
|
| 885 |
+
idx = match.start()
|
| 886 |
+
doc = doc[:idx] + script_tag + cleanup_tag + doc[idx:]
|
| 887 |
+
else:
|
| 888 |
+
# Append at end
|
| 889 |
+
doc = doc + script_tag + cleanup_tag
|
| 890 |
+
|
| 891 |
+
return doc
|
| 892 |
+
|
| 893 |
+
def send_transformers_to_sandbox(files: dict) -> str:
|
| 894 |
+
"""Build a self-contained HTML document from transformers.js files and return an iframe preview."""
|
| 895 |
+
merged_html = build_transformers_inline_html(files)
|
| 896 |
+
return send_to_sandbox(merged_html)
|
| 897 |
+
|
| 898 |
def parse_svelte_output(text):
|
| 899 |
"""Parse Svelte output to extract individual files"""
|
| 900 |
files = {
|
|
|
|
| 2308 |
yield {
|
| 2309 |
code_output: formatted_output,
|
| 2310 |
history: _history,
|
| 2311 |
+
sandbox: send_transformers_to_sandbox(files),
|
| 2312 |
history_output: history_to_chatbot_messages(_history),
|
| 2313 |
}
|
| 2314 |
else:
|
|
|
|
| 2552 |
yield {
|
| 2553 |
code_output: gr.update(value=formatted_output, language="html"),
|
| 2554 |
history_output: history_to_chatbot_messages(_history),
|
| 2555 |
+
sandbox: send_transformers_to_sandbox(files) if files['index.html'] else "<div style='padding:1em;color:#888;text-align:center;'>Preview is only available for HTML. Please download your code using the download button above.</div>",
|
| 2556 |
}
|
| 2557 |
elif has_existing_content:
|
| 2558 |
# Model is returning search/replace changes for transformers.js - apply them
|
| 2559 |
last_content = _history[-1][1] if _history and len(_history[-1]) > 1 else ""
|
| 2560 |
modified_content = apply_transformers_js_search_replace_changes(last_content, content)
|
| 2561 |
+
_mf = parse_transformers_js_output(modified_content)
|
| 2562 |
yield {
|
| 2563 |
code_output: gr.update(value=modified_content, language="html"),
|
| 2564 |
history_output: history_to_chatbot_messages(_history),
|
| 2565 |
+
sandbox: send_transformers_to_sandbox(_mf) if _mf['index.html'] else "<div style='padding:1em;color:#888;text-align:center;'>Preview is only available for HTML. Please download your code using the download button above.</div>",
|
| 2566 |
}
|
| 2567 |
else:
|
| 2568 |
# Still streaming, show partial content
|
|
|
|
| 2640 |
yield {
|
| 2641 |
code_output: formatted_output,
|
| 2642 |
history: _history,
|
| 2643 |
+
sandbox: send_transformers_to_sandbox(files),
|
| 2644 |
history_output: history_to_chatbot_messages(_history),
|
| 2645 |
}
|
| 2646 |
elif has_existing_content:
|
|
|
|
| 2648 |
last_content = _history[-1][1] if _history and len(_history[-1]) > 1 else ""
|
| 2649 |
modified_content = apply_transformers_js_search_replace_changes(last_content, content)
|
| 2650 |
_history.append([query, modified_content])
|
| 2651 |
+
_mf = parse_transformers_js_output(modified_content)
|
| 2652 |
yield {
|
| 2653 |
code_output: modified_content,
|
| 2654 |
history: _history,
|
| 2655 |
+
sandbox: send_transformers_to_sandbox(_mf),
|
| 2656 |
history_output: history_to_chatbot_messages(_history),
|
| 2657 |
}
|
| 2658 |
else:
|
|
|
|
| 3742 |
if language == "transformers.js":
|
| 3743 |
files = parse_transformers_js_output(code)
|
| 3744 |
if files['index.html']:
|
| 3745 |
+
return send_transformers_to_sandbox(files)
|
| 3746 |
return "<div style='padding:1em;color:#888;text-align:center;'>Preview is only available for HTML. Please download your code using the download button above.</div>"
|
| 3747 |
if language == "svelte":
|
| 3748 |
return "<div style='padding:1em;color:#888;text-align:center;'>Preview is only available for HTML. Please download your Svelte code and deploy it to see the result.</div>"
|