feat(playground): improve Playground chat experience and Markdown rendering (#5217)

* refactor(playground): streamline chat request state

- extract conversation actions from the page component to keep message flow logic reusable.
- unify streaming and non-streaming generation state, including abort support for non-stream requests.
- simplify message rendering and payload construction while localizing Playground prompts.

* fix(playground): validate persisted chat state

- wrap saved Playground state with a storage version while still reading legacy values.

- validate config, parameter toggles, and messages before restoring them from localStorage.

- cap stored chat history to the latest messages to avoid oversized or stale state.

* refactor(playground): centralize message content access

- route chat rendering, copy actions, and error display through shared message helpers.

- reuse the current-version update helper for non-streaming assistant responses.

- keep message version details behind utility functions to reduce future model churn.

* refactor(playground): split storage schemas

- move Playground storage validation schemas into a dedicated module.

- keep storage read and write logic focused on migration, trimming, and persistence.

- preserve the existing storage envelope and validation behavior.

* refactor(playground): extract options loading hook

- move model and group queries into a dedicated hook so the page component stays focused on layout wiring.
- preserve existing fallback selection and error toast behavior while reusing the hook through the playground barrel export.

* refactor(playground): extract prompt suggestions

- move static prompt suggestion rendering into a focused component so the input stays centered on compose controls.
- preserve translated suggestion submission behavior while isolating icon metadata from the input form.

* refactor(playground): extract input tools

- move attachment and search controls into a dedicated component so the prompt input stays focused on compose state.
- keep existing development toast behavior and disabled handling while centralizing tool metadata.

* refactor(playground): extract input controls

- move model, group, send, and stop controls into a focused component so the input only manages compose state.
- preserve existing disabled states and generation button behavior while isolating control rendering.

* refactor(playground): extract message content display

- move sources, reasoning, loading, error, and response rendering into a dedicated message content component.
- keep the chat list focused on message iteration, edit state, and action wiring without changing display behavior.

* refactor(playground): extract message editor

- move inline message editing controls into a dedicated editor component so the chat list stays focused on rendering flow.
- preserve save, save-and-submit, cancel, and disabled-state behavior for edited messages.

* refactor(playground): extract stream error parsing

- move SSE error payload parsing into a reusable stream utility so the request hook stays focused on lifecycle handling.
- preserve existing error message, error code, and fallback behavior for raw or empty stream errors.

* refactor(playground): extract request error parsing

- move non-stream request error extraction into a shared utility so the chat handler stays focused on request flow.
- preserve the existing response message, error code, and fallback priority for failed chat completions.

* refactor(playground): extract streaming chunk updates

- move reasoning and content chunk application into a message utility so the chat handler only wires stream events.
- preserve error-state skipping, reasoning accumulation, and content streaming behavior for assistant messages.

* refactor(playground): extract message reasoning parser

- move think tag parsing into a dedicated playground message utility.
- export the parser through the shared playground lib barrel for consistent imports.

* refactor(playground): extract message streaming utilities

- move stream chunk application and message finalization into a dedicated utility.
- keep stored message sanitization with the streaming lifecycle helpers.

* refactor(playground): extract message update utilities

- move assistant message update helpers into a focused playground utility.
- keep error-state message updates separate from core message construction helpers.

* refactor(playground): extract completion choice handling

- move non-streaming choice application into the message streaming utilities.
- keep the chat handler focused on request orchestration and message updates.

* refactor(playground): centralize assistant completion state

- add a helper for finalizing assistant messages with complete status.
- reuse the helper in stream completion and stop-generation paths.

* refactor(playground): extract stream message parsing

- move SSE delta parsing into a shared stream utility.
- keep the stream request hook focused on lifecycle handling and update dispatch.

* refactor(playground): extract stream ready state checks

- move SSE ready-state status handling into stream utilities.
- keep weak source status typing outside the stream request hook.

* refactor(playground): extract conversation message helpers

- move send, regenerate, and edit message list construction into focused utilities.
- keep the conversation hook focused on edit state and update dispatch.

* refactor(playground): extract state initialization helpers

- move playground initial state loading into focused utility helpers.
- centralize message state updater resolution outside the React state hook.

* refactor(playground): extract option fallback helpers

- move model and group fallback selection into focused playground utilities.
- keep the options hook focused on query results, toasts, and config updates.

* refactor(playground): extract message action helpers

- move message action state derivation into focused utilities.

- keep the action component focused on guarded handlers and rendering.

* refactor(playground): extract input control state

- move submit, stop, and selector state derivation into a pure helper.

- keep input controls focused on rendering model selectors and action buttons.

* refactor(playground): extract message content state

- move source, reasoning, loader, and body visibility checks into a pure helper.

- use a discriminated state shape so rendered reasoning content stays type-safe.

* refactor(playground): extract message editor state

- move save eligibility and submit visibility checks into a pure helper.

- keep the editor component focused on textarea and button rendering.

* refactor(playground): extract message error state

- move error kind, fallback content, and admin visibility checks into a pure helper.

- centralize the model pricing settings path used by the error action.

* refactor(playground): extract chat render state

- move editing content lookup and per-message render flags into conversation helpers.

- keep the chat component focused on mapping messages to editor and content views.

* refactor(playground): extract suggestion display state

- move suggestion class selection into a pure helper.

- keep the suggestions component focused on translation and rendering.

* refactor(playground): extract assistant message state checks

- move final and pending assistant status checks into streaming utilities.

- keep the chat handler focused on request lifecycle updates.

* refactor(playground): extract input tool state

- move attachment action metadata and development notices into input tool utilities.

- keep the input tools component focused on menu and button rendering.

* refactor(playground): extract stream protocol checks

- move SSE done-message and closed-ready-state checks into stream utilities.

- keep the stream request hook focused on event handling flow.

* refactor(playground): extract message removal helper

- move delete-message filtering into conversation message utilities.

- keep the conversation hook focused on action orchestration.

* refactor(playground): extract option error messages

- move option load error message selection into playground option utilities
- keep the options hook focused on query effects and fallback updates

* refactor(playground): extract input submit text helper

- move prompt submit text validation into input control utilities
- let the input component submit only when a concrete text value is available

* refactor(playground): centralize error message checks

- add a shared helper for identifying error messages
- remove direct status string checks from message content rendering

* refactor(playground): extract message content display checks

- move loader and content visibility decisions into local helper functions
- keep message content state assembly focused on composing render state

* refactor(playground): replace raw message role checks

- use shared message role constants in conversation edit handling
- avoid raw assistant role literals when validating API messages

* refactor(playground): extract non-stream response handling

- move chat completion response choice handling into message streaming utilities
- keep the chat handler focused on request lifecycle and error routing

* refactor(playground): centralize stream cleanup

- reuse one stream cleanup path for completion, errors, startup failures, and manual stops
- preserve the current-source guard when closing SSE streams

* refactor(playground): extract pending assistant check

- centralize pending assistant message detection in streaming utilities
- reuse the helper when sanitizing stored playground messages

* perf(playground): improve mobile input controls

- split mobile input controls into selector and action rows
- keep the desktop input footer compact while reducing mobile control crowding

* perf(playground): add starter empty state

- show starter prompts in the empty playground chat area
- wire empty-state prompt selection into the existing send flow
- add localized copy for the new empty state

* perf(playground): improve mobile message actions

- collapse mobile message actions into a touch-friendly dropdown menu
- keep the desktop hover action strip unchanged for pointer workflows
- share one action list between desktop buttons and the mobile menu

* perf(playground): add error recovery actions

- show retry, edit, and delete actions inside error message alerts
- route edit recovery to the previous user prompt when available
- keep recovery controls touch-friendly on mobile layouts

* perf(playground): refine message editing experience

- present message edits in a focused bordered editor panel
- add unsaved-change state, reset, and cancel confirmation flows
- improve mobile touch targets and keyboard shortcuts for editing

* perf(playground): improve markdown code blocks

- render fenced markdown code with syntax highlighting, line numbers, and fallback plain text
- add copy, download, and collapse controls for playground AI responses
- tighten code block layout and theme token styles for responsive markdown rendering

* fix(playground): constrain markdown code block height

- collapse long playground code blocks after a short preview instead of waiting for very large snippets
- cap expanded code blocks so long responses scroll inside the code block
- keep generic code block usage unconstrained unless a caller opts in

* feat(playground): add chat history clearing

- add a toolbar action that is enabled only when saved playground messages exist.
- confirm destructive clears before removing browser-stored conversation state.
- add localized strings for the action, dialog, and completion toast.

* perf(playground): improve chat markdown rendering

- refine assistant and user message surfaces so chat content matches the app UI.
- normalize markdown typography, tables, images, lists, blockquotes, and details rendering.
- add indentation cues for collapsible reasoning and source sections.

* style: format code block component

* style: format playground frontend files

* feat(playground): render markdown with stream parser

- replace Streamdown with stream-markdown-parser for project-owned markdown rendering and styling.
- split response rendering into focused block, inline, table, alert, details, and footnote modules.
- pass message final state into response parsing so streaming content can be parsed incrementally.

* fix(playground): localize reasoning and chat feedback

- translate reasoning status, message actions, playground errors, and response renderer fallbacks across supported locales.
- keep reasoning duration numeric and tighten the collapsible layout to prevent trigger jitter.
- register dynamic keys so i18n sync keeps runtime labels covered.

* refactor(playground): group files by functional area

- move chat, input, and message components into focused subdirectories to make the UI structure easier to scan.
- split playground helpers into input, message, streaming, storage, options, state, and suggestions modules.
- update barrel exports and imports so existing feature entry points continue to work.

* fix(playground): prevent history replay from freezing page

- defer saved conversation loading so route entry no longer blocks on localStorage parsing and markdown rendering.
- limit initial history rendering and skip expensive markdown parsing for oversized responses.
- normalize corrupted streaming snapshots and cumulative chunks to keep saved playground history bounded.
- add message timing metadata and layout alignment groundwork without introducing live timers.

* feat(playground): allow regenerating from user messages

- show regenerate actions on user messages with saved content.
- truncate following conversation state before starting a fresh assistant response.

* feat(playground): add raw response source view

- add a per-message source toggle for assistant responses.
- render raw response content with the existing code block viewer.
- localize the new source and preview action labels.

* feat(playground): render code with unified editor

- replace Shiki HTML rendering with a read-only CodeMirror view for code blocks and raw responses.
- reuse the same CodeMirror frame for message editing so source and edit modes stay visually aligned.
- add lightweight CodeMirror dependencies while keeping language support scoped to Markdown.

* perf(playground): streamline chat input controls

- combine model and group selection into one compact picker for faster context switching.
- switch playground action buttons to icon-first controls with tooltips to reduce toolbar width.
- refresh input footer styling and submit states so active and destructive actions are clearer.
- bump dompurify lockfile entry to keep the frontend dependency current.

* fix(playground): filter models by selected group

- query user models by the selected playground group instead of reusing the cross-group model union.
- clear unavailable model selections and block sending when the active group has no models.
- align model selector and error action controls with the existing playground interaction style.

* perf(playground): remove input suggestion chips

- remove the prompt suggestion row below the playground input to reduce visual noise.
- delete the now-unused suggestion component and display helper.

* perf(playground): stabilize reasoning trigger layout

- use fixed icon slots around the reasoning label so the left content stays still when toggling.
- limit the open state animation to the chevron rotation for a smoother collapse interaction.

* perf(playground): smooth reasoning expansion

- use the collapsible panel height animation for vertical reasoning reveals.
- sync inner content opacity and position with the panel state.
This commit is contained in:
QuentinHsu
2026-06-27 17:23:25 +08:00
committed by GitHub
parent df44a75d53
commit 966af88ec3
80 changed files with 6695 additions and 1801 deletions
+32
View File
@@ -280,6 +280,7 @@
"All models in use are properly configured.": "所有正在使用的模型都已正确配置。",
"All Must Match (AND)": "全部满足(AND",
"All nodes": "全部节点",
"All playground messages saved in this browser will be removed. This cannot be undone.": "保存在此浏览器中的所有游乐场消息都将被移除。此操作无法撤销。",
"All requests must include": "所有请求必须携带",
"All Status": "所有状态",
"All Sync Status": "所有同步状态",
@@ -340,6 +341,8 @@
"Amount the user pays to purchase this plan; the actual currency depends on the payment gateway.": "用户购买该套餐需支付的金额,具体币种由支付渠道决定",
"Amount to pay:": "待支付金额:",
"An unexpected error occurred": "发生意外错误",
"An unknown error occurred": "发生未知错误",
"Analyze data": "分析数据",
"and": "和",
"Announcement added. Click \"Save Settings\" to apply.": "公告已添加。点击 \"保存设置\" 以应用。",
"Announcement content": "公告内容",
@@ -526,6 +529,7 @@
"AZURE_OPENAI_ENDPOINT *": "AZURE_OPENAI_ENDPOINT *",
"Back": "返回",
"Back to Dashboard": "返回控制台",
"Back to footnote {{id}} reference": "返回脚注 {{id}} 引用处",
"Back to Home": "返回主页",
"Back to login": "返回登录",
"Back to Models": "返回模型",
@@ -798,6 +802,8 @@
"Clear All Cache": "清空全部缓存",
"Clear all filters": "清除所有筛选",
"Clear cache for this rule": "清空该规则缓存",
"Clear chat history": "清空聊天历史",
"Clear chat history?": "清空聊天历史?",
"Clear filters": "清除筛选器",
"Clear Mapping": "清除映射",
"Clear mode flags in prompts": "在提示中清除模式标志",
@@ -975,6 +981,7 @@
"Connect": "连接",
"Connect through OpenAI, Claude, Gemini, and other compatible API routes": "通过 OpenAI、Claude、Gemini 以及其他兼容 API 路由接入",
"Connected to io.net service normally.": "已正常连接 io.net 服务。",
"Connection closed": "连接已关闭",
"Connection error": "连接错误",
"Connection failed": "连接失败",
"Connection successful": "连接成功",
@@ -1007,6 +1014,7 @@
"Control which models are exposed and which groups may use them.": "控制对外暴露的模型,以及哪些分组可以使用它们。",
"Controls how much the model thinks before answering": "控制模型回答前的推理深度",
"Controls whether user verification (biometrics/PIN) is required during Passkey flows.": "控制在通行密钥流程中是否需要用户验证(生物识别/PIN)。",
"Conversation cleared": "对话已清空",
"Conversion rate from USD to your custom currency": "从美元到您的自定义货币的转换率",
"Convert reasoning_content to <think> tag in content": "将 reasoning_content 转换为 content 中的 <think> 标签",
"Convert string to lowercase": "把字符串转成小写",
@@ -1646,8 +1654,10 @@
"Equals": "等于",
"Error": "错误",
"Error Code (optional)": "错误代码(可选)",
"Error establishing connection": "建立连接失败",
"Error Message": "错误消息",
"Error Message (required)": "错误消息(必填)",
"Error parsing response data": "解析响应数据失败",
"Error Type (optional)": "错误类型(可选)",
"Estimated cost": "预计成本",
"Estimated quota cost": "估算配额费用",
@@ -2001,7 +2011,9 @@
"Generating new codes will invalidate all existing backup codes.": "生成新代码将使所有现有备份代码失效。",
"Generating...": "生成中...",
"Generation quality preset": "生成质量预设",
"Generation was interrupted": "生成已中断",
"Generic cache": "通用缓存",
"Get advice": "获取建议",
"Get notified when balance falls below this value": "当余额低于此值时接收通知",
"Get one here": "点此获取",
"Get started": "开始使用",
@@ -2175,6 +2187,7 @@
"Image In": "图像输入",
"Image input": "图片输入",
"Image input price": "图像输入价格",
"Image not available": "图片不可用",
"Image Out": "图像输出",
"Image output price": "图像输出价格",
"Image Preview": "图片预览",
@@ -2182,6 +2195,7 @@
"Image to Video": "图生视频",
"Image Tokens": "图像 Token",
"Import to CC Switch": "填入 CC Switch",
"Important": "重要",
"In Progress": "进行中",
"In:": "入:",
"incident": "次故障",
@@ -2384,6 +2398,7 @@
"Loading channel details": "正在加载渠道详情",
"Loading configuration": "正在加载配置",
"Loading content settings...": "正在加载内容设置...",
"Loading conversation...": "正在加载对话...",
"Loading current models...": "正在加载当前模型...",
"Loading failed": "加载失败",
"Loading maintenance settings...": "正在加载维护设置...",
@@ -2673,6 +2688,7 @@
"Needs API key": "需要 API 密钥",
"Nested JSON defining per-group rules for adding (+:), removing (-:), or appending usable groups.": "嵌套 JSON,定义按分组添加(+:)、移除(-:)或追加可用分组的规则。",
"Nested JSON: source group →": "嵌套 JSON:源分组 →",
"Network connection failed or server not responding": "网络连接失败或服务器未响应",
"Network proxy for this channel (supports socks5 protocol)": "此渠道的网络代理(支持 socks5 协议)",
"Never": "永不",
"Never expires": "永不过期",
@@ -2736,6 +2752,7 @@
"No conflicts match your search.": "没有冲突匹配您的搜索。",
"No console output": "无控制台输出",
"No containers": "无容器",
"No content to copy": "没有可复制的内容",
"No custom OAuth providers configured yet.": "尚未配置自定义 OAuth 提供商。",
"No data": "暂无数据",
"No Data": "无数据",
@@ -2879,6 +2896,7 @@
"Not tested": "未测试",
"Not used for upstream training by default": "默认不会用于上游训练",
"Not used yet": "暂未使用",
"Note": "备注",
"Notice": "通知",
"Notification Email": "通知邮箱",
"Notification Method": "通知方式",
@@ -3245,6 +3263,7 @@
"Please wait a moment before trying again.": "请稍候再试。",
"Please wait a moment, human check is initializing...": "请稍等,人机验证正在初始化...",
"Please wait before editing to avoid overwriting saved values.": "请等待加载完成后再编辑,以免覆盖已保存的值。",
"Please wait for the current generation to complete": "请等待当前生成完成",
"Policy JSON": "策略 JSON",
"Polling": "轮询",
"Polling mode requires Redis and memory cache, otherwise performance will be significantly degraded": "轮询模式需要 Redis 和内存缓存,否则性能将显著下降",
@@ -3440,6 +3459,7 @@
"Raw expression": "原始表达式",
"Raw JSON": "原始 JSON",
"Raw Quota": "原生额度",
"Raw response": "原始回复",
"Re-enable on success": "成功后重新启用",
"Re-login": "重新登录",
"Read channels": "读取渠道",
@@ -3578,6 +3598,7 @@
"Request conversion": "请求转换",
"Request Conversion": "请求转换",
"Request Count": "请求计数",
"Request error occurred": "请求发生错误",
"Request failed": "请求失败",
"Request flow": "请求流",
"Request Header Field": "请求头字段",
@@ -3649,6 +3670,7 @@
"Resetting...": "重置中...",
"Resolve Conflicts": "解决冲突",
"Resource Configuration": "资源配置",
"Responding...": "正在回复...",
"Resources": "资源",
"Response": "响应",
"Response Time": "响应时间",
@@ -3727,6 +3749,7 @@
"Sampling temperature; lower is more deterministic": "采样温度;越低越稳定",
"Sandbox mode": "沙盒模式",
"Save": "保存",
"Save & Submit": "保存并提交",
"Save all settings": "保存所有设置",
"Save Backup Codes": "保存备份代码",
"Save changes": "保存更改",
@@ -3959,9 +3982,11 @@
"Show all providers including unbound": "显示所有提供商(包括未绑定)",
"Show only bound providers": "仅显示已绑定的提供商",
"Show or hide flow columns": "显示或隐藏分流列",
"Show preview": "显示预览",
"Show prices in currency instead of quota.": "以货币而非配额显示价格。",
"Show sensitive data": "显示敏感数据",
"Show setup guide": "显示设置引导",
"Show source": "显示源码",
"Show token usage statistics in the UI": "在用户界面中显示令牌使用统计信息",
"Showcase core capabilities with demo credentials and limited access.": "使用演示凭据和有限访问权限展示核心功能。",
"Showing": "显示第",
@@ -4037,6 +4062,7 @@
"Standard price": "标准价格",
"Start": "开始",
"Start a conversation to see messages here": "开始对话以在此处查看消息",
"Start a playground chat": "开始一场游乐场对话",
"Start collecting payments globally without registering a company. Built for indie developers, OPC sole proprietorships, and startups. Waffo Pancake acts as your Merchant of Record, taking on the compliance burden of global payment collection — consumption tax, invoicing, subscription management, refunds, and chargebacks. Solo developers can launch fast and stay focused on product instead of compliance. Onboard in minutes — one prompt to a full integration.": "无需注册公司即可开始全球收款。面向独立开发者、OPC 个体经营者和初创团队构建。Waffo Pancake 作为你的登记商户(Merchant of Record),承担全球收款相关的合规负担,包括消费税、开票、订阅管理、退款和拒付。个人开发者可以快速上线,专注产品而不是合规事务。几分钟即可完成入驻,从一个提示词到完整集成。",
"Start for free with generous limits. No credit card required.": "免费开始使用,额度充足,无需绑定信用卡。",
"Start Time": "起始时间",
@@ -4118,6 +4144,7 @@
"Successfully enabled {{count}} model(s)": "成功启用 {{count}} 个模型",
"Suffix": "后缀",
"Suffix Match": "后缀匹配",
"Summarize text": "总结文本",
"SunoAPI": "SunoAPI",
"Sunset Glow": "日落霞光",
"Super Admin": "超级管理员",
@@ -4132,6 +4159,7 @@
"Supports HTML markup or iframe embedding. Enter HTML code directly, or provide a complete URL to automatically embed it as an iframe.": "支持 HTML 标记或 iframe 嵌入。直接输入 HTML 代码,或提供完整的 URL 以将其自动嵌入为 iframe。",
"Supports one-click configuration and perfectly adapts to NewAPI multi-protocol configuration.": "支持一键配置并完美适配 NewAPI 多协议配置",
"Supports PNG, JPG, SVG, or WebP. Recommended size: 128×128 or smaller.": "支持 PNG、JPG、SVG 或 WebP,建议尺寸不超过 128×128。",
"Surprise me": "给我惊喜",
"Sustained tokens per second": "持续每秒 Token 数",
"Swap Face": "换脸",
"Switch affinity on success": "成功后切换亲和",
@@ -4216,6 +4244,7 @@
"Test": "测试",
"Test {{count}} matching models": "测试 {{count}} 个匹配模型",
"Test {{count}} selected": "测试 {{count}} 个已选择项",
"Test a model with a starter prompt, or write your own request below.": "使用入门提示词测试模型,或在下方编写自己的请求。",
"Test all {{count}} models": "测试全部 {{count}} 个模型",
"Test All Channels": "测试所有渠道",
"Test Channel Connection": "测试渠道连接",
@@ -4280,6 +4309,7 @@
"These toggles affect whether certain request fields are passed through to the upstream provider.": "这些开关控制某些请求字段是否透传到上游服务。",
"Thinking Suffix Adapter": "思考后缀适配器",
"Thinking to Content": "思维到内容",
"Thinking...": "思考中...",
"Third-party account bindings (read-only, managed by user in profile settings)": "第三方账户绑定(只读,由用户在个人资料设置中管理)",
"Third-party Payment Config": "第三方支付配置",
"This action cannot be undone.": "此操作无法撤消。",
@@ -4336,6 +4366,7 @@
"This will update the priority to {{value}} for all {{count}} channel(s) with tag \"{{tag}}\". Continue?": "这会将标签 \"{{tag}}\" 下所有 {{count}} 个渠道的优先级更新为 {{value}}。继续吗?",
"This will update the weight to {{value}} for all {{count}} channel(s) with tag \"{{tag}}\". Continue?": "这会将标签 \"{{tag}}\" 下所有 {{count}} 个渠道的权重更新为 {{value}}。继续吗?",
"This year": "本年",
"Thought for {{duration}} seconds": "思考了 {{duration}} 秒",
"Three steps to get started": "三步快速上手",
"Throughput": "吞吐量",
"Throughput by group": "各分组吞吐量",
@@ -4359,6 +4390,7 @@
"Timeline": "时间线",
"times": "次",
"Timing": "耗时",
"Tip": "提示",
"to access this resource.": "访问此资源。",
"to confirm": "以确认",
"To Lower": "转小写",