diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index d3851b7..6c977e3 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -466,6 +466,165 @@ async def generate_title_using_ai( --- +## 🎭 iframe 主题检测规范 (iframe Theme Detection) + +当插件在 iframe 中运行(特别是使用 `srcdoc` 属性)时,需要检测应用程序的主题以保持视觉一致性。 + +### 检测优先级 (Priority Order) + +按以下顺序尝试检测主题,直到找到有效结果: + +1. **显式切换** (Explicit Toggle) - 用户手动点击主题按钮 +2. **父文档 Meta 标签** (Parent Meta Theme-Color) - 从 `window.parent.document` 的 `` 读取 +3. **父文档 Class/Data-Theme** (Parent HTML/Body Class) - 检查父文档 html/body 的 class 或 data-theme 属性 +4. **系统偏好** (System Preference) - `prefers-color-scheme: dark` 媒体查询 + +### 核心实现代码 (Implementation) + +```javascript +// 1. 颜色亮度解析(支持 hex 和 rgb) +const parseColorLuma = (colorStr) => { + if (!colorStr) return null; + // hex #rrggbb or rrggbb + let m = colorStr.match(/^#?([0-9a-f]{6})$/i); + if (m) { + const hex = m[1]; + const r = parseInt(hex.slice(0, 2), 16); + const g = parseInt(hex.slice(2, 4), 16); + const b = parseInt(hex.slice(4, 6), 16); + return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255; + } + // rgb(r, g, b) or rgba(r, g, b, a) + m = colorStr.match(/rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i); + if (m) { + const r = parseInt(m[1], 10); + const g = parseInt(m[2], 10); + const b = parseInt(m[3], 10); + return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255; + } + return null; +}; + +// 2. 从 meta 标签提取主题 +const getThemeFromMeta = (doc, scope = 'self') => { + const metas = Array.from((doc || document).querySelectorAll('meta[name="theme-color"]')); + if (!metas.length) return null; + const color = metas[metas.length - 1].content.trim(); + const luma = parseColorLuma(color); + if (luma === null) return null; + return luma < 0.5 ? 'dark' : 'light'; +}; + +// 3. 安全地访问父文档 +const getParentDocumentSafe = () => { + try { + if (!window.parent || window.parent === window) return null; + const pDoc = window.parent.document; + void pDoc.title; // 触发跨域检查 + return pDoc; + } catch (err) { + console.log(`Parent document not accessible: ${err.name}`); + return null; + } +}; + +// 4. 从父文档的 class/data-theme 检测主题 +const getThemeFromParentClass = () => { + try { + if (!window.parent || window.parent === window) return null; + const pDoc = window.parent.document; + const html = pDoc.documentElement; + const body = pDoc.body; + const htmlClass = html ? html.className : ''; + const bodyClass = body ? body.className : ''; + const htmlDataTheme = html ? html.getAttribute('data-theme') : ''; + + if (htmlDataTheme === 'dark' || bodyClass.includes('dark') || htmlClass.includes('dark')) + return 'dark'; + if (htmlDataTheme === 'light' || bodyClass.includes('light') || htmlClass.includes('light')) + return 'light'; + return null; + } catch (err) { + return null; + } +}; + +// 5. 主题设置及检测 +const setTheme = (wrapperEl, explicitTheme) => { + const parentDoc = getParentDocumentSafe(); + const metaThemeParent = parentDoc ? getThemeFromMeta(parentDoc, 'parent') : null; + const parentClassTheme = getThemeFromParentClass(); + const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + + // 按优先级选择 + const chosen = explicitTheme || metaThemeParent || parentClassTheme || (prefersDark ? 'dark' : 'light'); + wrapperEl.classList.toggle('theme-dark', chosen === 'dark'); + return chosen; +}; +``` + +### CSS 变量定义 (CSS Variables) + +使用 CSS 变量实现主题切换,避免硬编码颜色: + +```css +:root { + --primary-color: #1e88e5; + --background-color: #f4f6f8; + --text-color: #263238; + --border-color: #e0e0e0; +} + +.theme-dark { + --primary-color: #64b5f6; + --background-color: #111827; + --text-color: #e5e7eb; + --border-color: #374151; +} + +.container { + background-color: var(--background-color); + color: var(--text-color); + border-color: var(--border-color); +} +``` + +### 调试与日志 (Debugging) + +添加详细日志便于排查主题检测问题: + +```javascript +console.log(`[plugin] [parent] meta theme-color count: ${metas.length}`); +console.log(`[plugin] [parent] meta theme-color picked: "${color}"`); +console.log(`[plugin] [parent] meta theme-color luma=${luma.toFixed(3)}, inferred=${inferred}`); +console.log(`[plugin] parent html.class="${htmlClass}", data-theme="${htmlDataTheme}"`); +console.log(`[plugin] final chosen theme: ${chosen}`); +``` + +### 最佳实践 (Best Practices) + +- 仅尝试访问**父文档**的主题信息,不依赖 srcdoc iframe 自身的 meta(通常为空) +- 在跨域 iframe 中使用 class/data-theme 作为备选方案 +- 使用 try-catch 包裹所有父文档访问,避免跨域异常中断 +- 提供用户手动切换主题的按钮作为最高优先级 +- 记录详细日志便于用户反馈主题检测问题 + +### OpenWebUI Configuration Requirement (OpenWebUI Configuration) + +For iframe plugins to access parent document theme information, users need to configure: + +1. **Enable Artifact Same-Origin Access** - In User Settings: **Interface** → **Artifacts** → Check **iframe Sandbox Allow Same Origin** +2. **Configure Sandbox Attributes** - Ensure iframe's sandbox attribute includes both `allow-same-origin` and `allow-scripts` +3. **Verify Meta Tag** - Ensure OpenWebUI page head contains `` tag + +**Important Notes**: +- Same-origin access allows iframe to read theme information via `window.parent.document` +- Cross-origin iframes cannot access parent document and should implement class/data-theme detection as fallback +- Using same-origin access in srcdoc iframe is safe (origin is null, doesn't bypass CORS policy) +- Users can provide manual theme toggle button in plugin as highest priority option + +--- + ## ✅ 开发检查清单 (Development Checklist) 开发新插件时,请确保完成以下检查: diff --git a/docs/examples/action_plugin_smart_mind_map_example_cn.md b/docs/examples/action_plugin_smart_mind_map_example_cn.md index eb99c7a..8908684 100644 --- a/docs/examples/action_plugin_smart_mind_map_example_cn.md +++ b/docs/examples/action_plugin_smart_mind_map_example_cn.md @@ -1,8 +1,8 @@ -# Open WebUI Action 插件开发范例:智绘心图 +# Open WebUI Action 插件开发范例:思维导图 ## 引言 -“智绘心图” (`smart-mind-map`) 是一个功能强大的 Open WebUI Action 插件。它通过分析用户提供的文本,利用大语言模型(LLM)提取关键信息,并最终生成一个可交互的、可视化的思维导图。本文档将深入解析其源码 (`思维导图.py`),提炼其中蕴含的插件开发知识与最佳实践,为开发者提供一个高质量的参考范例。 +“思维导图” (`smart-mind-map`) 是一个功能强大的 Open WebUI Action 插件。它通过分析用户提供的文本,利用大语言模型(LLM)提取关键信息,并最终生成一个可交互的、可视化的思维导图。本文档将深入解析其源码 (`思维导图.py`),提炼其中蕴含的插件开发知识与最佳实践,为开发者提供一个高质量的参考范例。 ## 核心开发知识点 @@ -22,15 +22,18 @@ Open WebUI 通过文件顶部的特定格式注释来识别和展示插件信息。 **代码示例 (`思维导图.py`):** + ```python """ -title: 智绘心图 +title: 思维导图 icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+CiAgPGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIi8+CiAgPGxpbmUgeDE9IjEyIiB5MT0iOSIgeDI9IjEyIiB5Mj0iNCIvPgogIDxjaXJjbGUgY3g9IjEyIiBjeT0iMyIgcj0iMS41Ii8+CiAgPGxpbmUgeDE9IjEyIiB5MT0iMTUiIHgyPSIxMiIgeTI9IjIwIi8+CiAgPGNpcmNsZSBjeD0iMTIiIGN5PSIyMSIgcj0iMS41Ii8+CiAgPGxpbmUgeDE9IjkiIHkxPSIxMiIgeDI9IjQiIHkyPSIxMiIvPgogIDxjaXJjbGUgY3g9IjMiIGN5PSIxMiIgcj0iMS41Ii8+CiAgPGxpbmUgeDE9IjE1IiB5MT0iMTIiIHgyPSIyMCIgeTI9IjEyIi8+CiAgPGNpcmNsZSBjeD0iMjEiIGN5PSIxMiIgcj0iMS41Ii8+CiAgPGxpbmUgeDE9IjEwLjUiIHkxPSྡ1LjUiIHgyPSI2IiB5Mj0iNiIvPgogIDxjaXJjbGUgY3g9IjUiIGN5PSI1Iigcj0iMS41Ii8+CiAgPGxpbmUgeDE9IjEzLjUiIHkxPSྡ5LjUgeDI9IjE1IiB5Mj0iNiIvPgogIDxjaXJjbGUgY3g9IjE5IiBjeT0iNSIgcj0iMS41Ii8+CiAgPGxpbmUgeDE9ྡ1LjUgeTE9ྡ3MuNSB4Mj0iNiIgeTI9IjE4Ii8+CiAgPGNpcmNsZSBjeD0iNSIgY3k9IjE5IiByPSྡ1LjUiLz4KICA8bGluZSB4MT0ྡzIuNSB5MT0ྡzIuNSB4Mj0iNSIgeTI9IjE4Ii8+CiAgPGNpcmNsZSBjeD0ྡ5IiBjeT0ྡ5IiByPSྡ1LjUiLz4KPC9zdmc+Cg== version: 0.7.2 description: 智能分析文本内容,生成交互式思维导图,帮助用户结构化和可视化知识。 """ ``` + **知识点**: + - `title`: 插件在 UI 中显示的名称。 - `icon_url`: 插件的图标,支持 base64 编码的 SVG,以实现无依赖的矢量图标。 - `version`: 插件的版本号。 @@ -43,6 +46,7 @@ description: 智能分析文本内容,生成交互式思维导图,帮助用户 通过在 `Action` 类内部定义一个 `Valves` Pydantic 模型,可以为插件创建可在 Web UI 中配置的参数。 **代码示例 (`思维导图.py`):** + ```python class Action: class Valves(BaseModel): @@ -60,7 +64,9 @@ class Action: def __init__(self): self.valves = self.Valves() ``` + **知识点**: + - `Valves` 类继承自 `pydantic.BaseModel`。 - 每个字段都是一个配置项,`default` 是默认值,`description` 会在 UI 中作为提示信息显示。 - 在 `__init__` 中实例化 `self.valves`,之后可以通过 `self.valves.PARAMETER_NAME` 来访问配置值。 @@ -72,6 +78,7 @@ class Action: `action` 方法是插件的执行入口,它是一个异步函数,接收 Open WebUI 传入的上下文信息。 **代码示例 (`思维导图.py`):** + ```python async def action( self, @@ -83,7 +90,9 @@ class Action: # ... 插件逻辑 ... return body ``` + **知识点**: + - `body`: 包含当前聊天上下文的字典,最重要的是 `body.get("messages")`,它包含了完整的消息历史。 - `__user__`: 包含当前用户信息的字典,如 `id`, `name`, `language` 等。插件中演示了如何兼容其为 `dict` 或 `list` 的情况。 - `__event_emitter__`: 一个可调用的异步函数,用于向前端发送事件,是实现实时反馈的关键。 @@ -97,6 +106,7 @@ class Action: 使用 `__event_emitter__` 可以极大地提升用户体验,让用户了解插件的执行进度。 **代码示例 (`思维导图.py`):** + ```python # 发送通知 (Toast) await __event_emitter__( @@ -104,7 +114,7 @@ await __event_emitter__( "type": "notification", "data": { "type": "info", # 'info', 'success', 'warning', 'error' - "content": "智绘心图已启动,正在为您生成思维导图...", + "content": "思维导图已启动,正在为您生成思维导图...", }, } ) @@ -114,7 +124,7 @@ await __event_emitter__( { "type": "status", "data": { - "description": "智绘心图: 深入分析文本结构...", + "description": "思维导图: 深入分析文本结构...", "done": False, # False 表示进行中 "hidden": False, }, @@ -126,14 +136,16 @@ await __event_emitter__( { "type": "status", "data": { - "description": "智绘心图: 绘制完成!", + "description": "思维导图: 绘制完成!", "done": True, # True 表示已完成 "hidden": False, # True 可以让成功状态自动隐藏 }, } ) ``` + **知识点**: + - **通知 (`notification`)**: 在屏幕角落弹出短暂的提示信息,适合用于触发、成功或失败的即时反馈。 - **状态 (`status`)**: 在聊天输入框上方显示一个持久的状态条,适合展示多步骤任务的当前进度。`done: True` 会标记任务完成。 @@ -141,9 +153,10 @@ await __event_emitter__( ### 5. 与 LLM 交互 -插件的核心功能通常依赖于 LLM。`智绘心图` 演示了如何构建一个结构化的 Prompt 并调用 LLM。 +插件的核心功能通常依赖于 LLM。`思维导图` 演示了如何构建一个结构化的 Prompt 并调用 LLM。 **代码示例 (`思维导图.py`):** + ```python # 1. 构建动态 Prompt SYSTEM_PROMPT_MINDMAP_ASSISTANT = "..." # 系统指令 @@ -179,7 +192,9 @@ llm_response = await generate_chat_completion( assistant_response_content = llm_response["choices"][0]["message"]["content"] markdown_syntax = self._extract_markdown_syntax(assistant_response_content) ``` + **知识点**: + - **Prompt 工程**: 将系统指令和用户指令分离。在用户指令中动态注入上下文信息(如用户名、时间、语言),可以使 LLM 的输出更具个性化和准确性。 - **调用工具**: 使用 `open_webui.utils.chat.generate_chat_completion` 是与 Open WebUI 内置 LLM 服务交互的标准方式。 - **用户上下文**: 调用 `generate_chat_completion` 需要传递 `user_obj`,这可能用于权限控制、计费或模型特定的用户标识。通过 `open_webui.models.users.Users.get_user_by_id` 获取该对象。 @@ -192,6 +207,7 @@ markdown_syntax = self._extract_markdown_syntax(assistant_response_content) Action 插件的一大亮点是能够生成 HTML,从而在聊天界面中渲染丰富的交互式内容。 **代码示例 (`思维导图.py`):** + ```python # 1. 定义 HTML 模板 HTML_TEMPLATE_MINDMAP = """ @@ -227,9 +243,11 @@ final_html_content = html_embed_tag = f"```html\n{final_html_content}\n```" body["messages"][-1]["content"] = f"{long_text_content}\n\n{html_embed_tag}" ``` + **知识点**: + - **HTML 模板**: 将静态 HTML/CSS/JS 代码定义为模板字符串,使用占位符(如 `{unique_id}`)来注入动态数据。 -- **嵌入 JS**: 可以在 HTML 中直接嵌入 JavaScript 代码,用于处理前端交互逻辑,如渲染图表、绑定按钮事件等。`智绘心图` 的 JS 代码负责调用 Markmap.js 库来渲染思维导图,并实现了“复制 SVG”和“复制 Markdown”的按钮功能。 +- **嵌入 JS**: 可以在 HTML 中直接嵌入 JavaScript 代码,用于处理前端交互逻辑,如渲染图表、绑定按钮事件等。`思维导图` 的 JS 代码负责调用 Markmap.js 库来渲染思维导图,并实现了“复制 SVG”和“复制 Markdown”的按钮功能。 - **唯一 ID**: 使用 `unique_id` 是一个好习惯,可以防止在同一页面上多次使用该插件时发生 DOM 元素 ID 冲突。 - **响应格式**: 最终的 HTML 内容需要被包裹在 ````html\n...\n```` 代码块中,Open WebUI 的前端会自动识别并渲染它。 - **内容追加**: 插件将生成的 HTML 追加到原始用户输入之后,而不是替换它,保留了上下文。 @@ -241,6 +259,7 @@ body["messages"][-1]["content"] = f"{long_text_content}\n\n{html_embed_tag}" 一个生产级的插件必须具备良好的健壮性。 **代码示例 (`思维导图.py`):** + ```python # 输入验证 if len(long_text_content) < self.valves.MIN_TEXT_LENGTH: @@ -251,8 +270,8 @@ if len(long_text_content) < self.valves.MIN_TEXT_LENGTH: try: # ... 核心逻辑 ... except Exception as e: - error_message = f"智绘心图处理失败: {str(e)}" - logger.error(f"智绘心图错误: {error_message}", exc_info=True) + error_message = f"思维导图处理失败: {str(e)}" + logger.error(f"思维导图错误: {error_message}", exc_info=True) # 向前端发送错误通知 if __event_emitter__: @@ -267,7 +286,9 @@ logger = logging.getLogger(__name__) logger.info("Action started") logger.error("Error occurred", exc_info=True) ``` + **知识点**: + - **输入验证**: 在执行核心逻辑前,对输入(如文本长度)进行检查,可以避免不必要的资源消耗和潜在错误。 - **`try...except` 块**: 将主要逻辑包裹在 `try` 块中,并捕获 `Exception`,确保任何意外失败都能被优雅地处理。 - **用户友好的错误反馈**: 在 `except` 块中,不仅要记录详细的错误日志(`logger.error`),还要通过 `EventEmitter` 和聊天消息向用户提供清晰、可操作的错误提示。 @@ -277,9 +298,10 @@ logger.error("Error occurred", exc_info=True) ### 总结 -`智绘心图` 插件是一个优秀的 Open WebUI Action 开发学习案例。它全面展示了如何利用 Action 插件的各项功能,构建一个交互性强、用户体验好、功能完整且健壮的 AI 应用。 +`思维导图` 插件是一个优秀的 Open WebUI Action 开发学习案例。它全面展示了如何利用 Action 插件的各项功能,构建一个交互性强、用户体验好、功能完整且健壮的 AI 应用。 **最佳实践总结**: + - **明确元数据**: 为你的插件提供清晰的 `title`, `icon`, `description`。 - **提供配置**: 使用 `Valves` 让插件更灵活。 - **善用反馈**: 积极使用 `EventEmitter` 提供实时状态和通知。 @@ -288,4 +310,4 @@ logger.error("Error occurred", exc_info=True) - **防御性编程**: 始终考虑输入验证和错误处理。 - **详细日志**: 记录日志是排查问题的关键。 -通过学习和借鉴`智绘心图`的设计模式,开发者可以更高效地构建出属于自己的高质量 Open WebUI 插件。 +通过学习和借鉴`思维导图`的设计模式,开发者可以更高效地构建出属于自己的高质量 Open WebUI 插件。 diff --git a/plugins/actions/README_CN.md b/plugins/actions/README_CN.md index 4c011b2..b17d6a3 100644 --- a/plugins/actions/README_CN.md +++ b/plugins/actions/README_CN.md @@ -8,7 +8,7 @@ | 插件名称 | 描述 | 版本 | 文档 | | :----------- | :----------------------------------- | :---- | :---------------------------------------------------------------------------- | -| **智绘心图** | 智能分析文本内容,生成交互式思维导图 | 0.7.2 | [中文](./smart-mind-map/README_CN.md) / [English](./smart-mind-map/README.md) | +| **思维导图** | 智能分析文本内容,生成交互式思维导图 | 0.7.2 | [中文](./smart-mind-map/README_CN.md) / [English](./smart-mind-map/README.md) | ## 🎯 什么是动作插件? diff --git a/plugins/actions/smart-mind-map/README.md b/plugins/actions/smart-mind-map/README.md index 8c3a409..b538d33 100644 --- a/plugins/actions/smart-mind-map/README.md +++ b/plugins/actions/smart-mind-map/README.md @@ -10,12 +10,12 @@ Smart Mind Map is a powerful OpenWebUI action plugin that intelligently analyzes ## Core Features -- ✅ **Intelligent Text Analysis**: Automatically identifies core themes, key concepts, and hierarchical structures -- ✅ **Interactive Visualization**: Generates beautiful interactive mind maps based on Markmap.js -- ✅ **Multi-language Support**: Automatically adjusts output based on user language -- ✅ **Real-time Rendering**: Renders mind maps directly in the chat interface without navigation -- ✅ **Export Capabilities**: Supports copying SVG code and Markdown source -- ✅ **Customizable Configuration**: Configurable LLM model, minimum text length, and other parameters +- ✅ **Intelligent Text Analysis**: Automatically identifies core themes, key concepts, and hierarchical structures +- ✅ **Interactive Visualization**: Generates beautiful interactive mind maps based on Markmap.js +- ✅ **Multi-language Support**: Automatically adjusts output based on user language +- ✅ **Real-time Rendering**: Renders mind maps directly in the chat interface without navigation +- ✅ **Export Capabilities**: Supports copying SVG code and Markdown source +- ✅ **Customizable Configuration**: Configurable LLM model, minimum text length, and other parameters --- @@ -43,14 +43,24 @@ Smart Mind Map is a powerful OpenWebUI action plugin that intelligently analyzes The plugin requires access to an LLM model for text analysis. Please ensure: -- Your OpenWebUI instance has at least one available LLM model configured -- Recommended to use fast, economical models (e.g., `gemini-2.5-flash`) for the best experience -- Configure the `LLM_MODEL_ID` parameter in the plugin settings +- Your OpenWebUI instance has at least one available LLM model configured +- Recommended to use fast, economical models (e.g., `gemini-2.5-flash`) for the best experience +- Configure the `LLM_MODEL_ID` parameter in the plugin settings ### 3. Plugin Activation Select the "Smart Mind Map" action plugin in chat settings to enable it. +### 4. Theme Color Consistency (Optional) + +To keep the mind map visually consistent with the OpenWebUI theme colors, enable same-origin access for artifacts in OpenWebUI: + +- **Configuration Location**: In OpenWebUI User Settings: **Interface** → **Artifacts** → **iframe Sandbox Allow Same Origin** +- **Enable Option**: Check the "Allow same-origin access for artifacts" / "iframe sandbox allow-same-origin" option +- **Sandbox Attributes**: Ensure the iframe's sandbox attribute includes both `allow-same-origin` and `allow-scripts` + +Once enabled, the mind map will automatically detect and apply the current OpenWebUI theme (light/dark) without any manual configuration. + --- ## Configuration Parameters @@ -77,6 +87,7 @@ You can adjust the following parameters in the plugin's settings (Valves): ### Usage Example **Input Text:** + ``` Artificial Intelligence (AI) is a branch of computer science dedicated to creating systems capable of performing tasks that typically require human intelligence. Main application areas include: @@ -102,20 +113,20 @@ Generated mind maps support two export methods: ### Frontend Rendering -- **Markmap.js**: Open-source mind mapping rendering engine -- **D3.js**: Data visualization foundation library -- **Responsive Design**: Adapts to different screen sizes +- **Markmap.js**: Open-source mind mapping rendering engine +- **D3.js**: Data visualization foundation library +- **Responsive Design**: Adapts to different screen sizes ### Backend Processing -- **LLM Integration**: Calls configured models via `generate_chat_completion` -- **Text Preprocessing**: Automatically filters HTML code blocks, extracts plain text content -- **Format Conversion**: Converts LLM output to Markmap-compatible Markdown format +- **LLM Integration**: Calls configured models via `generate_chat_completion` +- **Text Preprocessing**: Automatically filters HTML code blocks, extracts plain text content +- **Format Conversion**: Converts LLM output to Markmap-compatible Markdown format ### Security -- **XSS Protection**: Automatically escapes `` tags to prevent script injection -- **Input Validation**: Checks text length to avoid invalid requests +- **XSS Protection**: Automatically escapes `` tags to prevent script injection +- **Input Validation**: Checks text length to avoid invalid requests --- @@ -124,72 +135,78 @@ Generated mind maps support two export methods: ### Issue: Plugin Won't Start **Solution:** -- Check OpenWebUI logs for error messages -- Confirm the plugin is correctly uploaded and enabled -- Verify OpenWebUI version supports action plugins + +- Check OpenWebUI logs for error messages +- Confirm the plugin is correctly uploaded and enabled +- Verify OpenWebUI version supports action plugins ### Issue: Text Content Too Short **Symptom:** Prompt shows "Text content is too short for effective analysis" **Solution:** -- Ensure input text contains at least 100 characters (default configuration) -- Lower the `MIN_TEXT_LENGTH` parameter value in plugin settings -- Provide more detailed, structured text content + +- Ensure input text contains at least 100 characters (default configuration) +- Lower the `MIN_TEXT_LENGTH` parameter value in plugin settings +- Provide more detailed, structured text content ### Issue: Mind Map Not Generated **Solution:** -- Check if `LLM_MODEL_ID` is configured correctly -- Confirm the configured model is available in OpenWebUI -- Review backend logs for LLM call failures -- Verify user has sufficient permissions to access the configured model + +- Check if `LLM_MODEL_ID` is configured correctly +- Confirm the configured model is available in OpenWebUI +- Review backend logs for LLM call failures +- Verify user has sufficient permissions to access the configured model ### Issue: Mind Map Display Error **Symptom:** Shows "⚠️ Mind map rendering failed" **Solution:** -- Check browser console for error messages -- Confirm Markmap.js and D3.js libraries are loading correctly -- Verify generated Markdown format conforms to Markmap specifications -- Try refreshing the page to re-render + +- Check browser console for error messages +- Confirm Markmap.js and D3.js libraries are loading correctly +- Verify generated Markdown format conforms to Markmap specifications +- Try refreshing the page to re-render ### Issue: Export Function Not Working **Solution:** -- Confirm browser supports Clipboard API -- Check if browser is blocking clipboard access permissions -- Use modern browsers (Chrome, Firefox, Edge, etc.) + +- Confirm browser supports Clipboard API +- Check if browser is blocking clipboard access permissions +- Use modern browsers (Chrome, Firefox, Edge, etc.) --- ## Best Practices 1. **Text Preparation** - - Provide text content with clear structure and distinct hierarchies - - Use paragraphs, lists, and other formatting to help LLM understand text structure - - Avoid excessively lengthy or unstructured text + - Provide text content with clear structure and distinct hierarchies + - Use paragraphs, lists, and other formatting to help LLM understand text structure + - Avoid excessively lengthy or unstructured text 2. **Model Selection** - - For daily use, recommend fast models like `gemini-2.5-flash` - - For complex text analysis, use more powerful models (e.g., GPT-4) - - Balance speed and analysis quality based on needs + - For daily use, recommend fast models like `gemini-2.5-flash` + - For complex text analysis, use more powerful models (e.g., GPT-4) + - Balance speed and analysis quality based on needs 3. **Performance Optimization** - - Set `MIN_TEXT_LENGTH` appropriately to avoid processing text that's too short - - For particularly long texts, consider summarizing before generating mind maps - - Disable `show_status` in production environments to reduce interface updates + - Set `MIN_TEXT_LENGTH` appropriately to avoid processing text that's too short + - For particularly long texts, consider summarizing before generating mind maps + - Disable `show_status` in production environments to reduce interface updates --- ## Changelog ### v0.7.2 (Current Version) -- Optimized text extraction logic, automatically filters HTML code blocks -- Improved error handling and user feedback -- Enhanced export functionality compatibility -- Optimized UI styling and interactive experience + +- Optimized text extraction logic, automatically filters HTML code blocks +- Improved error handling and user feedback +- Enhanced export functionality compatibility +- Optimized UI styling and interactive experience --- @@ -205,6 +222,6 @@ Welcome to submit issue reports and improvement suggestions! Please visit the pr ## Related Resources -- [Markmap Official Website](https://markmap.js.org/) -- [OpenWebUI Documentation](https://docs.openwebui.com/) -- [D3.js Official Website](https://d3js.org/) +- [Markmap Official Website](https://markmap.js.org/) +- [OpenWebUI Documentation](https://docs.openwebui.com/) +- [D3.js Official Website](https://d3js.org/) diff --git a/plugins/actions/smart-mind-map/README_CN.md b/plugins/actions/smart-mind-map/README_CN.md index 3372ee8..188f9fa 100644 --- a/plugins/actions/smart-mind-map/README_CN.md +++ b/plugins/actions/smart-mind-map/README_CN.md @@ -1,21 +1,21 @@ -# 智绘心图 - 思维导图生成插件 +# 思维导图 - 思维导图生成插件 **作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 0.7.2 | **许可证:** MIT > **重要提示**:为了确保所有插件的可维护性和易用性,每个插件都应附带清晰、完整的文档,以确保其功能、配置和使用方法得到充分说明。 -智绘心图是一个强大的 OpenWebUI 动作插件,能够智能分析长篇文本内容,自动生成交互式思维导图,帮助用户结构化和可视化知识。 +思维导图是一个强大的 OpenWebUI 动作插件,能够智能分析长篇文本内容,自动生成交互式思维导图,帮助用户结构化和可视化知识。 --- ## 核心特性 -- ✅ **智能文本分析**: 自动识别文本的核心主题、关键概念和层次结构 -- ✅ **交互式可视化**: 基于 Markmap.js 生成美观的交互式思维导图 -- ✅ **多语言支持**: 根据用户语言自动调整输出 -- ✅ **实时渲染**: 在聊天界面中直接渲染思维导图,无需跳转 -- ✅ **导出功能**: 支持复制 SVG 代码和 Markdown 源码 -- ✅ **自定义配置**: 可配置 LLM 模型、最小文本长度等参数 +- ✅ **智能文本分析**: 自动识别文本的核心主题、关键概念和层次结构 +- ✅ **交互式可视化**: 基于 Markmap.js 生成美观的交互式思维导图 +- ✅ **多语言支持**: 根据用户语言自动调整输出 +- ✅ **实时渲染**: 在聊天界面中直接渲染思维导图,无需跳转 +- ✅ **导出功能**: 支持复制 SVG 代码和 Markdown 源码 +- ✅ **自定义配置**: 可配置 LLM 模型、最小文本长度等参数 --- @@ -43,13 +43,23 @@ 插件需要访问 LLM 模型来分析文本。请确保: -- 您的 OpenWebUI 实例中配置了至少一个可用的 LLM 模型 -- 推荐使用快速、经济的模型(如 `gemini-2.5-flash`)来获得最佳体验 -- 在插件设置中配置 `LLM_MODEL_ID` 参数 +- 您的 OpenWebUI 实例中配置了至少一个可用的 LLM 模型 +- 推荐使用快速、经济的模型(如 `gemini-2.5-flash`)来获得最佳体验 +- 在插件设置中配置 `LLM_MODEL_ID` 参数 ### 3. 插件启用 -在聊天设置中选择"智绘心图"动作插件即可启用。 +在聊天设置中选择"思维导图"动作插件即可启用。 + +### 4. 主题颜色风格一致性(可选) + +为了使思维导图与 OpenWebUI 主题颜色风格保持一致,需要在 OpenWebUI 中启用 artifact 的同源访问: + +- **配置位置**:在 OpenWebUI 用户设置中找到"界面"→"产物"部分(Settings → Interface → Products/Artifacts) +- **启用选项**:勾选 "iframe 沙盒允许同源访问"(Allow same-origin access for artifacts / iframe sandbox allow-same-origin) +- **沙箱属性**:确保 iframe 的 sandbox 属性包含 `allow-same-origin` 和 `allow-scripts` + +启用后,思维导图会自动检测并应用 OpenWebUI 的当前主题(亮色/暗色),无需手动配置。 --- @@ -69,7 +79,7 @@ ### 基本使用 -1. 在聊天设置中启用"智绘心图"动作 +1. 在聊天设置中启用"思维导图"动作 2. 在对话中输入或粘贴长篇文本内容(至少 100 字符) 3. 发送消息后,插件会自动分析并生成思维导图 4. 思维导图将在聊天界面中直接渲染显示 @@ -77,6 +87,7 @@ ### 使用示例 **输入文本:** + ``` 人工智能(AI)是计算机科学的一个分支,致力于创建能够执行通常需要人类智能的任务的系统。 主要应用领域包括: @@ -102,20 +113,20 @@ ### 前端渲染 -- **Markmap.js**: 开源的思维导图渲染引擎 -- **D3.js**: 数据可视化基础库 -- **响应式设计**: 适配不同屏幕尺寸 +- **Markmap.js**: 开源的思维导图渲染引擎 +- **D3.js**: 数据可视化基础库 +- **响应式设计**: 适配不同屏幕尺寸 ### 后端处理 -- **LLM 集成**: 通过 `generate_chat_completion` 调用配置的模型 -- **文本预处理**: 自动过滤 HTML 代码块,提取纯文本内容 -- **格式转换**: 将 LLM 输出转换为 Markmap 兼容的 Markdown 格式 +- **LLM 集成**: 通过 `generate_chat_completion` 调用配置的模型 +- **文本预处理**: 自动过滤 HTML 代码块,提取纯文本内容 +- **格式转换**: 将 LLM 输出转换为 Markmap 兼容的 Markdown 格式 ### 安全性 -- **XSS 防护**: 自动转义 `` 标签,防止脚本注入 -- **输入验证**: 检查文本长度,避免无效请求 +- **XSS 防护**: 自动转义 `` 标签,防止脚本注入 +- **输入验证**: 检查文本长度,避免无效请求 --- @@ -124,72 +135,78 @@ ### 问题:插件无法启动 **解决方案:** -- 检查 OpenWebUI 日志,查看是否有错误信息 -- 确认插件已正确上传并启用 -- 验证 OpenWebUI 版本是否支持动作插件 + +- 检查 OpenWebUI 日志,查看是否有错误信息 +- 确认插件已正确上传并启用 +- 验证 OpenWebUI 版本是否支持动作插件 ### 问题:文本内容过短 **现象:** 提示"文本内容过短,无法进行有效分析" **解决方案:** -- 确保输入的文本至少包含 100 个字符(默认配置) -- 可以在插件设置中降低 `MIN_TEXT_LENGTH` 参数值 -- 提供更详细、结构化的文本内容 + +- 确保输入的文本至少包含 100 个字符(默认配置) +- 可以在插件设置中降低 `MIN_TEXT_LENGTH` 参数值 +- 提供更详细、结构化的文本内容 ### 问题:思维导图未生成 **解决方案:** -- 检查 `LLM_MODEL_ID` 是否配置正确 -- 确认配置的模型在 OpenWebUI 中可用 -- 查看后端日志,检查是否有 LLM 调用失败的错误 -- 验证用户是否有足够的权限访问配置的模型 + +- 检查 `LLM_MODEL_ID` 是否配置正确 +- 确认配置的模型在 OpenWebUI 中可用 +- 查看后端日志,检查是否有 LLM 调用失败的错误 +- 验证用户是否有足够的权限访问配置的模型 ### 问题:思维导图显示错误 **现象:** 显示"⚠️ 思维导图渲染失败" **解决方案:** -- 检查浏览器控制台的错误信息 -- 确认 Markmap.js 和 D3.js 库是否正确加载 -- 验证生成的 Markdown 格式是否符合 Markmap 规范 -- 尝试刷新页面重新渲染 + +- 检查浏览器控制台的错误信息 +- 确认 Markmap.js 和 D3.js 库是否正确加载 +- 验证生成的 Markdown 格式是否符合 Markmap 规范 +- 尝试刷新页面重新渲染 ### 问题:导出功能不工作 **解决方案:** -- 确认浏览器支持剪贴板 API -- 检查浏览器是否阻止了剪贴板访问权限 -- 使用现代浏览器(Chrome、Firefox、Edge 等) + +- 确认浏览器支持剪贴板 API +- 检查浏览器是否阻止了剪贴板访问权限 +- 使用现代浏览器(Chrome、Firefox、Edge 等) --- ## 最佳实践 1. **文本准备** - - 提供结构清晰、层次分明的文本内容 - - 使用段落、列表等格式帮助 LLM 理解文本结构 - - 避免过于冗长或无结构的文本 + - 提供结构清晰、层次分明的文本内容 + - 使用段落、列表等格式帮助 LLM 理解文本结构 + - 避免过于冗长或无结构的文本 2. **模型选择** - - 对于日常使用,推荐 `gemini-2.5-flash` 等快速模型 - - 对于复杂文本分析,可以使用更强大的模型(如 GPT-4) - - 根据需求平衡速度和分析质量 + - 对于日常使用,推荐 `gemini-2.5-flash` 等快速模型 + - 对于复杂文本分析,可以使用更强大的模型(如 GPT-4) + - 根据需求平衡速度和分析质量 3. **性能优化** - - 合理设置 `MIN_TEXT_LENGTH`,避免处理过短的文本 - - 对于特别长的文本,考虑先进行摘要再生成思维导图 - - 在生产环境中关闭 `show_status` 以减少界面更新 + - 合理设置 `MIN_TEXT_LENGTH`,避免处理过短的文本 + - 对于特别长的文本,考虑先进行摘要再生成思维导图 + - 在生产环境中关闭 `show_status` 以减少界面更新 --- ## 更新日志 ### v0.7.2 (当前版本) -- 优化文本提取逻辑,自动过滤 HTML 代码块 -- 改进错误处理和用户反馈 -- 增强导出功能的兼容性 -- 优化 UI 样式和交互体验 + +- 优化文本提取逻辑,自动过滤 HTML 代码块 +- 改进错误处理和用户反馈 +- 增强导出功能的兼容性 +- 优化 UI 样式和交互体验 --- @@ -205,6 +222,6 @@ ## 相关资源 -- [Markmap 官方网站](https://markmap.js.org/) -- [OpenWebUI 文档](https://docs.openwebui.com/) -- [D3.js 官方网站](https://d3js.org/) +- [Markmap 官方网站](https://markmap.js.org/) +- [OpenWebUI 文档](https://docs.openwebui.com/) +- [D3.js 官方网站](https://d3js.org/) diff --git a/plugins/actions/smart-mind-map/思维导图.py b/plugins/actions/smart-mind-map/思维导图.py index 282f47b..c0a8e3d 100644 --- a/plugins/actions/smart-mind-map/思维导图.py +++ b/plugins/actions/smart-mind-map/思维导图.py @@ -1,18 +1,20 @@ """ -title: 智绘心图 +title: 思维导图 icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHg9IjE2IiB5PSIxNiIgd2lkdGg9IjYiIGhlaWdodD0iNiIgcng9IjEiLz48cmVjdCB4PSIyIiB5PSIxNiIgd2lkdGg9IjYiIGhlaWdodD0iNiIgcng9IjEiLz48cmVjdCB4PSI5IiB5PSIyIiB3aWR0aD0iNiIgaGVpZ2h0PSI2IiByeD0iMSIvPjxwYXRoIGQ9Ik01IDE2di0zYTEgMSAwIDAgMSAxLTFoMTJhMSAxIDAgMCAxIDEgMXYzIi8+PHBhdGggZD0iTTEyIDEyVjgiLz48L3N2Zz4= -version: 0.7.4 +version: 0.8.0 description: 智能分析文本内容,生成交互式思维导图,帮助用户结构化和可视化知识。 """ -from pydantic import BaseModel, Field -from typing import Optional, Dict, Any import logging -import time +import os import re +import time +from datetime import datetime, timezone +from typing import Any, Dict, Optional +from zoneinfo import ZoneInfo + from fastapi import Request -from datetime import datetime -import pytz +from pydantic import BaseModel, Field from open_webui.utils.chat import generate_chat_completion from open_webui.models.users import Users @@ -75,24 +77,20 @@ HTML_WRAPPER_TEMPLATE = """ } #main-container { display: flex; - flex-wrap: wrap; + flex-direction: column; gap: 20px; - align-items: flex-start; + align-items: stretch; width: 100%; } .plugin-item { - flex: 1 1 400px; /* 默认宽度,允许伸缩 */ - min-width: 300px; + width: 100%; border-radius: 12px; - overflow: hidden; + overflow: visible; transition: all 0.3s ease; } .plugin-item:hover { transform: translateY(-2px); } - @media (max-width: 768px) { - .plugin-item { flex: 1 1 100%; } - } /* STYLES_INSERTION_POINT */ @@ -115,13 +113,24 @@ CSS_TEMPLATE_MINDMAP = """ --muted-text-color: #546e7a; --border-color: #e0e0e0; --header-gradient: linear-gradient(135deg, var(--secondary-color), var(--primary-color)); - --shadow: 0 10px 25px rgba(0, 0, 0, 0.08); + --shadow: 0 10px 20px rgba(0, 0, 0, 0.06); --border-radius: 12px; --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; } + .theme-dark { + --primary-color: #64b5f6; + --secondary-color: #81c784; + --background-color: #111827; + --card-bg-color: #1f2937; + --text-color: #e5e7eb; + --muted-text-color: #9ca3af; + --border-color: #374151; + --header-gradient: linear-gradient(135deg, #0ea5e9, #22c55e); + --shadow: 0 10px 20px rgba(0, 0, 0, 0.3); + } .mindmap-container-wrapper { font-family: var(--font-family); - line-height: 1.7; + line-height: 1.6; color: var(--text-color); margin: 0; padding: 0; @@ -130,99 +139,110 @@ CSS_TEMPLATE_MINDMAP = """ height: 100%; display: flex; flex-direction: column; + background: var(--background-color); + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + box-shadow: var(--shadow); } .header { background: var(--header-gradient); color: white; - padding: 20px 24px; + padding: 18px 20px; text-align: center; + border-top-left-radius: var(--border-radius); + border-top-right-radius: var(--border-radius); } .header h1 { margin: 0; - font-size: 1.5em; + font-size: 1.4em; font-weight: 600; - text-shadow: 0 1px 3px rgba(0,0,0,0.2); + letter-spacing: 0.3px; } .user-context { - font-size: 0.8em; + font-size: 0.85em; color: var(--muted-text-color); - background-color: #eceff1; - padding: 8px 16px; + background-color: rgba(255, 255, 255, 0.6); + padding: 8px 14px; display: flex; - justify-content: space-around; + justify-content: space-between; flex-wrap: wrap; border-bottom: 1px solid var(--border-color); + gap: 6px; } - .user-context span { margin: 2px 8px; } - .content-area { - padding: 20px; + .theme-dark .user-context { + background-color: rgba(31, 41, 55, 0.7); + } + .user-context span { margin: 2px 6px; } + .content-area { + padding: 16px; flex-grow: 1; + background: var(--card-bg-color); } .markmap-container { position: relative; - background-color: #fff; - background-image: radial-gradient(var(--border-color) 0.5px, transparent 0.5px); - background-size: 20px 20px; - border-radius: 8px; - padding: 16px; + background-color: var(--card-bg-color); + border-radius: 10px; + padding: 12px; display: flex; justify-content: center; align-items: center; border: 1px solid var(--border-color); - box-shadow: inset 0 2px 6px rgba(0,0,0,0.03); + width: 100%; + min-height: 60vh; + overflow: visible; } - .download-area { - text-align: center; - padding-top: 20px; - margin-top: 20px; - border-top: 1px solid var(--border-color); + .control-rows { + display: flex; + flex-wrap: wrap; + gap: 10px; + justify-content: center; + margin-top: 12px; } - .download-btn { + .btn-group { + display: inline-flex; + gap: 6px; + align-items: center; + } + .control-btn { background-color: var(--primary-color); color: white; border: none; - padding: 8px 16px; - border-radius: 6px; + padding: 8px 12px; + border-radius: 8px; font-size: 0.9em; font-weight: 500; cursor: pointer; - transition: all 0.2s ease-in-out; - margin: 0 6px; + transition: background-color 0.15s ease, transform 0.15s ease; display: inline-flex; align-items: center; gap: 6px; } - .download-btn.secondary { - background-color: var(--secondary-color); - } - .download-btn:hover { - transform: translateY(-1px); - box-shadow: 0 4px 8px rgba(0,0,0,0.1); - } - .download-btn.copied { - background-color: #2e7d32; - } + .control-btn.secondary { background-color: var(--secondary-color); } + .control-btn.neutral { background-color: #64748b; } + .control-btn:hover { transform: translateY(-1px); } + .control-btn.copied { background-color: #2e7d32; } + .control-btn:disabled { opacity: 0.6; cursor: not-allowed; } .footer { text-align: center; - padding: 16px; - font-size: 0.8em; - color: #90a4ae; - background-color: #eceff1; + padding: 12px; + font-size: 0.85em; + color: var(--muted-text-color); + background-color: var(--card-bg-color); border-top: 1px solid var(--border-color); + border-bottom-left-radius: var(--border-radius); + border-bottom-right-radius: var(--border-radius); } .footer a { color: var(--primary-color); text-decoration: none; font-weight: 500; } - .footer a:hover { - text-decoration: underline; - } + .footer a:hover { text-decoration: underline; } .error-message { color: #c62828; background-color: #ffcdd2; border: 1px solid #ef9a9a; - padding: 16px; + padding: 14px; border-radius: 8px; font-weight: 500; font-size: 1em; @@ -240,15 +260,29 @@ CONTENT_TEMPLATE_MINDMAP = """
-
- - +
+
+ + + +
+
+ + + +
+
+ + + + +