ling-series-spaces / tab_code.py
GitHub Action
Sync ling-space changes from GitHub commit 8b86524
5442d39
raw
history blame
7.74 kB
from tkinter import Label
import gradio as gr
import logging
from config import CHAT_MODEL_SPECS, LING_1T, CODE_FRAMEWORK_SPECS, STATIC_PAGE
from ui_components.model_selector import create_model_selector
from ui_components.code_framework_selector import create_code_framework_selector
from code_kit.agent_code_generator import code_generation_agent
from code_kit.agent_style_generator import generate_random_style
from code_kit.code_examples import CODE_EXAMPLES
# Configure logging
logger = logging.getLogger(__name__)
def refresh_preview(code_type_display_name, current_code, chatbot_history):
"""
Refresh the preview and add a log entry.
Refactored to rely solely on the HTML content for preview, assuming all frameworks
produce a single-file HTML output that works in an iframe.
"""
logger.info(f"--- [Manual Refresh] Start ---")
logger.info(f"Code Type: {code_type_display_name}")
# Simple validation: Check if code seems to be HTML
if not current_code or not isinstance(current_code, str):
chatbot_history.append({"role": "assistant", "content": "⚠️ **警告**: 没有代码可供刷新。"})
return gr.update(), chatbot_history
# For all currently supported frameworks (Static, React+Tailwind, R3F, Old School),
# the output is a self-contained HTML string.
# So we can process them uniformly.
escaped_code = current_code.replace("'", "'").replace('"', '"')
final_preview_html = f"""
<div style="width: 100%; height: 600px; border: 1px solid #ddd; overflow: hidden; position: relative; background-color: #f9f9f9;">
<iframe srcdoc='{escaped_code}'
style="position: absolute; top: 0; left: 0; width: 200%; height: 200%; transform: scale(0.5); transform-origin: 0 0; border: none;">
</iframe>
</div>
"""
chatbot_history.append({"role": "assistant", "content": "🔄 **状态**: 预览已手动刷新。"})
logger.info("Refreshed preview.")
return gr.HTML(final_preview_html), chatbot_history
def toggle_fullscreen(is_fullscreen):
is_fullscreen = not is_fullscreen
new_button_text = "退出全屏" if is_fullscreen else "全屏预览"
panel_visibility = not is_fullscreen
return is_fullscreen, gr.update(value=new_button_text), gr.update(visible=panel_visibility)
def log_js_error(error_text, chatbot_history):
"""Appends a JavaScript error received from the frontend to the log chatbot."""
if not error_text:
return chatbot_history
formatted_error = f"🚨 **在预览中发现运行时异常!**\n```\n{error_text}\n```"
# Check if the last message is the same error to prevent flooding
if chatbot_history and chatbot_history[-1]["content"] == formatted_error:
return chatbot_history
chatbot_history.append({"role": "assistant", "content": formatted_error})
return chatbot_history
def create_code_tab():
# Inject custom CSS to hide components
gr.HTML("""
<style>
.hidden-component {
display: none;
}
</style>
""")
fullscreen_state = gr.State(False)
with gr.Row(elem_id="indicator-code-tab"):
with gr.Column(scale=1) as left_panel:
with gr.Column(scale=1): # Settings Panel
code_framework_dropdown = create_code_framework_selector(
framework_specs=CODE_FRAMEWORK_SPECS,
default_framework_constant=STATIC_PAGE
)
model_choice_dropdown, model_description_markdown = create_model_selector(
model_specs=CHAT_MODEL_SPECS,
default_model_constant=LING_1T
)
prompt_input = gr.Textbox(lines=5, placeholder="例如:创建一个带标题和按钮的简单页面", label="提示词")
overall_style_input = gr.Textbox(label="整体风格", placeholder="例如:类似一本羊皮纸的书", lines=2)
decoration_input = gr.Textbox(label="装饰风格", placeholder="例如:粗边框,无圆角", lines=2)
# Hidden textbox to store the raw palette string for the prompt
palette_input = gr.Textbox(label="Palette (Raw)", elem_classes="hidden-component")
palette_display = gr.HTML(value='<div style="color: #999; font-size: 12px;">(生成风格后展示)</div>',
container=True,
label="风格色板",
show_label=True)
generate_style_btn = gr.Button("🎲 随机生成风格", size="sm")
with gr.Column():
gr.Markdown("### 示例")
examples_dataset = gr.Dataset(
components=[gr.Textbox(visible=False)],
samples=[[item["task"]] for item in CODE_EXAMPLES],
label="示例",
headers=["选择一个示例"],
)
generate_button = gr.Button("生成代码", variant="primary")
with gr.Column(scale=4):
with gr.Tabs(elem_id="result_tabs") as result_tabs:
with gr.TabItem("实时预览", id=0):
with gr.Row():
gr.Markdown("### 实时预览")
fullscreen_button = gr.Button("全屏预览", scale=0)
preview_output = gr.HTML(value="<p>预览将在此处显示。</p>")
with gr.TabItem("生成的源代码", id=1):
gr.Markdown("### 生成的源代码")
code_output = gr.Code(language="html", label="生成的代码", interactive=True)
refresh_button = gr.Button("刷新预览")
with gr.Column(scale=1):
log_chatbot = gr.Chatbot(label="生成日志", height=300)
js_error_channel = gr.Textbox(visible=True, elem_classes=["js_error_channel"], label="Debug Error Channel", interactive=False)
# Event Handler for Example Selection
def on_select_example(evt: gr.SelectData):
selected_task = evt.value[0]
item = next((i for i in CODE_EXAMPLES if i["task"] == selected_task), None)
if not item:
return gr.update(), gr.update()
return gr.update(value=item["user_prompt"]), gr.update(value=item["model"])
examples_dataset.select(
fn=on_select_example,
inputs=None,
outputs=[prompt_input, model_choice_dropdown],
show_progress="none"
)
# Event Handler for Style Generation
generate_style_btn.click(
fn=generate_random_style,
inputs=[model_choice_dropdown],
outputs=[palette_display, palette_input, decoration_input, overall_style_input]
)
refresh_button.click(
fn=refresh_preview,
inputs=[code_framework_dropdown, code_output, log_chatbot],
outputs=[preview_output, log_chatbot]
)
generate_button.click(
fn=code_generation_agent,
inputs=[
code_framework_dropdown,
model_choice_dropdown,
prompt_input,
palette_input, # Pass the raw palette string
decoration_input,
overall_style_input,
log_chatbot
],
outputs=[code_output, preview_output, log_chatbot, result_tabs]
)
fullscreen_button.click(
fn=toggle_fullscreen,
inputs=[fullscreen_state],
outputs=[fullscreen_state, fullscreen_button, left_panel]
)
js_error_channel.change(
fn=log_js_error,
inputs=[js_error_channel, log_chatbot],
outputs=log_chatbot
)