diff --git a/CHANGELOG.md b/CHANGELOG.md index e5579c2832..d38676eef3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.8.7] - 2026-03-01 + +### Fixed + +- 🔒 **Connection access control privacy.** Tool server and terminal connections without explicit access grants are now private (admin-only) by default, fixing a bug where connections configured with no access grants were visible to all users instead of being restricted. [Commit](https://github.com/open-webui/open-webui/commit/2751a0f0b) +- 🧠 **ChatControls memory leak.** The ChatControls panel no longer leaks event listeners, ResizeObserver instances, and media query handlers when navigating between chats, fixing memory accumulation that could degrade performance during extended use. [#22112](https://github.com/open-webui/open-webui/pull/22112) +- 💾 **Temporary chat params preservation.** Model parameters are now correctly saved when creating a temporary chat, ensuring custom settings like temperature and top_p persist across the session. [Commit](https://github.com/open-webui/open-webui/commit/fe837d80e) +- ⚡ **Faster artifact content updates.** Artifact content extraction during streaming is now debounced via requestAnimationFrame, reducing redundant DOM reads and improving CPU efficiency when tokens arrive faster than the browser can paint. [Commit](https://github.com/open-webui/open-webui/commit/6863ca482) + ## [0.8.6] - 2026-03-01 ### Added diff --git a/backend/open_webui/utils/access_control/__init__.py b/backend/open_webui/utils/access_control/__init__.py index 232bd20d60..a228bbd6a8 100644 --- a/backend/open_webui/utils/access_control/__init__.py +++ b/backend/open_webui/utils/access_control/__init__.py @@ -163,8 +163,8 @@ def has_connection_access( based on ``config.access_grants`` within the connection dict. - Admin with BYPASS_ADMIN_ACCESS_CONTROL → always allowed - - Empty / missing access_grants → allowed for all users - - Otherwise → delegates to ``has_access`` + - Missing, None, or empty access_grants → private, admin-only + - access_grants has entries → delegates to ``has_access`` """ from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL @@ -175,9 +175,6 @@ def has_connection_access( user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user.id)} access_grants = (connection.get("config") or {}).get("access_grants", []) - if not access_grants: - return True - return has_access(user.id, "read", access_grants, user_group_ids) diff --git a/package-lock.json b/package-lock.json index 7681c6b6f9..14ce3d6500 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "open-webui", - "version": "0.8.6", + "version": "0.8.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "open-webui", - "version": "0.8.6", + "version": "0.8.7", "dependencies": { "@azure/msal-browser": "^4.5.0", "@codemirror/lang-javascript": "^6.2.2", diff --git a/package.json b/package.json index 8477f654b4..c0fef7e548 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-webui", - "version": "0.8.6", + "version": "0.8.7", "private": true, "scripts": { "dev": "npm run pyodide:fetch && vite dev --host", diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index a44756febd..4d8d06f54c 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -964,7 +964,11 @@ }; $: if (history) { - getContents(); + cancelAnimationFrame(contentsRAF); + contentsRAF = requestAnimationFrame(() => { + getContents(); + contentsRAF = null; + }); } else { artifactContents.set([]); } @@ -1294,6 +1298,7 @@ }; let scrollRAF = null; + let contentsRAF = null; const scheduleScrollToBottom = () => { if (!scrollRAF) { scrollRAF = requestAnimationFrame(async () => { @@ -2719,6 +2724,7 @@ id: uuidv4(), title: title.length > 50 ? `${title.slice(0, 50)}...` : title, models: selectedModels, + params: params, history: history, messages: messages, timestamp: Date.now() diff --git a/src/lib/components/chat/ChatControls.svelte b/src/lib/components/chat/ChatControls.svelte index e5398f7f91..d7f546c228 100644 --- a/src/lib/components/chat/ChatControls.svelte +++ b/src/lib/components/chat/ChatControls.svelte @@ -50,9 +50,8 @@ export let files; export let modelId; - export let pane; + export let pane: Pane | null = null; - let mediaQuery; let largeScreen = false; let dragged = false; let minSize = 0; @@ -172,56 +171,67 @@ dragged = false; }; - onMount(async () => { - mediaQuery = window.matchMedia('(min-width: 1024px)'); + onMount(() => { + const mediaQuery = window.matchMedia('(min-width: 1024px)'); mediaQuery.addEventListener('change', handleMediaQuery); handleMediaQuery(mediaQuery); + let resizeObserver: ResizeObserver | null = null; + let isDestroyed = false; + // Wait for Svelte to render the Pane after largeScreen changed - await tick(); + const init = async () => { + await tick(); - const container = document.getElementById('chat-container'); - minSize = Math.floor((350 / container.clientWidth) * 100); + if (isDestroyed) return; - const resizeObserver = new ResizeObserver((entries) => { - for (let entry of entries) { - const width = entry.contentRect.width; - minSize = Math.floor((350 / width) * 100); - if ($showControls) { - if (pane && pane.isExpanded() && pane.getSize() < minSize) { - pane.resize(minSize); - } else { - let size = Math.floor( - (parseInt(localStorage?.chatControlsSize) / container.clientWidth) * 100 - ); - if (size < minSize) pane.resize(minSize); + // If controls were persisted as open, set the pane to the saved size + if ($showControls && pane) { + openPane(); + } + + setTimeout(() => { + paneReady = true; + }, 0); + + const container = document.getElementById('chat-container') as HTMLElement; + if (!container) return; + + minSize = Math.floor((350 / container.clientWidth) * 100); + resizeObserver = new ResizeObserver((entries) => { + for (let entry of entries) { + const width = entry.contentRect.width; + minSize = Math.floor((350 / width) * 100); + if ($showControls) { + if (pane && pane.isExpanded() && pane.getSize() < minSize) { + pane.resize(minSize); + } else { + let size = Math.floor( + (parseInt(localStorage?.chatControlsSize) / container.clientWidth) * 100 + ); + if (size < minSize && pane) pane.resize(minSize); + } } } - } - }); - resizeObserver.observe(container); + }); + resizeObserver.observe(container); + }; + init(); document.addEventListener('mousedown', onMouseDown); document.addEventListener('mouseup', onMouseUp); - setTimeout(() => { - paneReady = true; - }, 0); - - // If controls were persisted as open, set the pane to the saved size - if ($showControls && pane) { - openPane(); - } - }); - - onDestroy(() => { - paneReady = false; - if (!largeScreen) { - showControls.set(false); - } - mediaQuery.removeEventListener('change', handleMediaQuery); - document.removeEventListener('mousedown', onMouseDown); - document.removeEventListener('mouseup', onMouseUp); + return () => { + isDestroyed = true; + paneReady = false; + resizeObserver?.disconnect(); + if (!largeScreen) { + showControls.set(false); + } + mediaQuery.removeEventListener('change', handleMediaQuery); + document.removeEventListener('mousedown', onMouseDown); + document.removeEventListener('mouseup', onMouseUp); + }; }); const closeHandler = () => {