Open WebUI 交互设计指南:5个让你用着顺手的界面细节
【免费下载链接】open-webuiUser-friendly AI Interface (Supports Ollama, OpenAI API, ...)项目地址: https://gitcode.com/GitHub_Trending/op/open-webui
Open WebUI 是一款自托管的 AI 聊天界面,接上 Ollama 或 OpenAI API 之后,你在浏览器里就能用上大模型。用上一段时间你会发现,它顺手的地方不在特效,而在一堆不起眼的小设计。这篇指南按真实使用场景拆开讲 5 处交互设计,帮你明白它为什么用起来这么顺。
第一次打开 Open WebUI:加载页与侧边栏怎么设计
加载等待是新手最容易流失的时刻,Open WebUI 把启动阶段做成了"有明确进度"的画面,而不是一闪一闪的空白页。
src/app.html里直接写死了这个启动画面的骨架:中间是 logo,下面一根进度条。
<div id="progress-bar" style="position: absolute; width: 0%; height: 0.75rem; border-radius: 9999px; background-color: #fff;" class="bg-white" ></div>这段就是启动时看到的那根白色进度条,宽度从 0% 起步,由加载脚本驱动增长。同一份文件里还有一段脚本,在页面渲染前就读系统深色模式偏好,决定先显示亮色还是深色启动图——你还没点进界面,主题就已经跟手了。
侧边栏开合是同样的思路:不是一闪出现的硬切,而是用 200 毫秒的宽度过渡动画(transition-width duration-200,见src/lib/components/chat/Chat.svelte),收起时空间平滑让出来,眼睛不用重新找内容。
拖文件进聊天框:拖的不只是附件
大多数人把"拖拽"理解成上传附件,但 Open WebUI 把整个输入框都变成了落点,落下来的东西比你想的多。
const textData = e.dataTransfer?.getData('text/plain'); if (textData) { const data = JSON.parse(textData); if (data.type === 'chat' && data.id) { const chat = await getChatById(localStorage.token, data.id); if (chat) { const chatItem = { type: 'chat', id: chat.id, name: chat.title }; if (!files.find((f) => f.id === chatItem.id)) { files = [...files, chatItem]; } } } }这是src/lib/components/chat/MessageInput.svelte里的拖放处理。落下来的是"聊天"类型时,它不当文件传,而是把那条历史对话拉过来当参考资料。也就是说,你可以从左侧边栏直接把旧对话拖进输入框,让模型接着旧话题答。拖拽在这里不是上传通道,是一条"引用"通道。
还有个容易忽略的细节:dragleave事件会先判断指针是否真的离开了输入框,没离开就不取消高亮。你拖着文件在框里挪动时,边框不会一抖一抖,而是稳定的一次提示。
写长消息、说长话:超长粘贴与语音输入
长内容最容易把聊天界面搞乱:粘贴一大段文本、开口说一分钟话,普通实现都容易翻车。Open WebUI 在这里做了两件事。
超长粘贴自动变文件
开启largeTextAsFile设置后,往输入框粘贴超长文本时,它不会被塞进输入框把界面撑爆,而是自动转成一个文件附件。代码里有个长度阈值判断,超过就拦截粘贴、改走文件通道。你看到的是一张文件卡片出现在输入框上方,而不是满屏文字。
🎧 语音说完直接变文字
<VoiceRecording bind:recording onConfirm={async (data) => { const { text, filename } = data; recording = false; await insertTextAtCursor(`${text}`); if ($settings?.speechAutoSend ?? false) { dispatch('submit', prompt); } }} />这段嵌在src/lib/components/chat/MessageInput.svelte里。录完的语音被转写成文字,插回你正在写的输入框,而不是直接发出去——你可以先改再发。开了speechAutoSend设置,转写完就直接发送。先给控制权、再给自动化,这是它最稳的地方。
键盘盲操作:一键发送的快捷键怎么定
聊天界面是少数"手不离键盘"就能完成大部分操作的工具,Open WebUI 把发送、换行、取消都留给了键盘。
// Depending on the user's settings, it will send the message // either when Enter is pressed or when Ctrl+Enter is pressed. const enterPressed = ($settings?.ctrlEnterToSend ?? false) ? (e.key === 'Enter' || e.keyCode === 13) && isCtrlPressed : (e.key === 'Enter' || e.keyCode === 13) && !e.shiftKey; if (enterPressed) { e.preventDefault(); if (prompt !== '' || files.length > 0) { dispatch('submit', prompt); } }这段就是src/lib/components/chat/MessageInput.svelte的发送逻辑本体:默认 Enter 发送、Shift+Enter 换行,改成 Ctrl+Enter 发送只要一个开关。还有个容易漏掉的兼容:代码专门处理了中日文输入法的选词阶段,中文用户按回车选词时不会误发消息。盲打用户最烦的就是"我选个字结果发出去了"这种事故。
⌨️ Esc 键也有用:在输入框里按 Esc 会清空当前选中的模型、工具等附加项,等于"一键回到干净状态"。
看不见屏幕也能用:读屏与深色模式
无障碍不是加个标签就完事,而是每个图标按钮都得能被读出来。Open WebUI 导航栏是一堆纯图标按钮,每个都带描述:
<button class="flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition" on:click={async () => { await showControls.set(!$showControls); }} aria-label="Controls" >这段在src/lib/components/chat/Navbar.svelte里。屏幕上是个图标,屏幕阅读器念出来的是 "Controls"——按钮干啥的一目了然。新建聊天、开关侧边栏等按钮同理,各有各的aria-label。
深色模式方面,src/app.html开头那段脚本同时响应系统主题和手动设置,还备了纯黑的 OLED 模式;手机上靠 viewport 设置和触摸点判断,把"回车发送"这类桌面习惯自动关掉,换成更适合手指的操作。
加载、拖拽、长内容、键盘、读屏,这五个最容易出问题的环节各自磨一遍,Open WebUI 的"顺手"就是这么攒出来的。想自己看代码,聊天相关的组件都摆在同一个目录里。
- 官方文档:README.md
- 聊天组件源码:src/lib/components/chat/
- 后端接口源码:backend/open_webui/routers/
【免费下载链接】open-webuiUser-friendly AI Interface (Supports Ollama, OpenAI API, ...)项目地址: https://gitcode.com/GitHub_Trending/op/open-webui
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考