Merge pull request #22121 from open-webui/dev

0.8.7
This commit is contained in:
Tim Baek
2026-03-02 05:14:08 +04:00
committed by GitHub
6 changed files with 71 additions and 49 deletions
+9
View File
@@ -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
@@ -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)
+2 -2
View File
@@ -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",
+1 -1
View File
@@ -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",
+7 -1
View File
@@ -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()
+50 -40
View File
@@ -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 = () => {