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>"
|