From eba2b2cd7257640e98305d60c6712a6f01fed809 Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Tue, 31 Mar 2026 09:54:28 +0800 Subject: [PATCH 001/334] refactor(SVGPanZoom): Fix memory leaking - consolidate zoom lifecycle into PanzoomContainer (#23236) * refactor(SVGPanZoom): attach panzoom via use: action and remove unused parent bind:this * refactor: centralize panzoom in createPanzoomAction and align ImagePreview cleanup * refactor(panzoom): consolidate zoom lifecycle into PanzoomContainer and remove action-based wiring --- src/lib/components/chat/FileNav.svelte | 1 - .../chat/FileNav/FilePreview.svelte | 49 ++++++------------- .../components/common/FileItemModal.svelte | 26 ++-------- src/lib/components/common/ImagePreview.svelte | 40 ++++----------- .../components/common/PanzoomContainer.svelte | 33 +++++++++++++ src/lib/components/common/SVGPanZoom.svelte | 32 ++++-------- 6 files changed, 72 insertions(+), 109 deletions(-) create mode 100644 src/lib/components/common/PanzoomContainer.svelte diff --git a/src/lib/components/chat/FileNav.svelte b/src/lib/components/chat/FileNav.svelte index 988c92669c..2a14312d45 100644 --- a/src/lib/components/chat/FileNav.svelte +++ b/src/lib/components/chat/FileNav.svelte @@ -293,7 +293,6 @@ // ── File preview management ────────────────────────────────────────── const clearFilePreview = () => { fileContent = null; - filePreviewRef?.disposePanzoom(); if (fileImageUrl) { URL.revokeObjectURL(fileImageUrl); fileImageUrl = null; diff --git a/src/lib/components/chat/FileNav/FilePreview.svelte b/src/lib/components/chat/FileNav/FilePreview.svelte index 0a7250c3ed..0bfa4fe504 100644 --- a/src/lib/components/chat/FileNav/FilePreview.svelte +++ b/src/lib/components/chat/FileNav/FilePreview.svelte @@ -1,6 +1,5 @@
{:else if fileImageUrl !== null} -
+ {selectedFile?.split('/').pop()} -
+ {:else if fileVideoUrl !== null}
@@ -343,9 +323,10 @@
{:else if fileOfficeSlides !== null && fileOfficeSlides.length > 0}
-
-
+ {#if fileOfficeSlides.length > 1}
{ - pzInstance = panzoom(node, { - bounds: true, - boundsPadding: 0.1, - zoomSpeed: 0.065 - }); - }; - + let panzoomRef: PanzoomContainer; const resetImageView = () => { - if (pzInstance) { - pzInstance.moveTo(0, 0); - pzInstance.zoomAbs(0, 0, 1); - } + panzoomRef?.reset(); }; $: isPDF = @@ -266,10 +254,6 @@ if (item?.context === 'full') { enableFullContent = true; } - - return () => { - pzInstance?.dispose(); - }; }); @@ -445,7 +429,7 @@
-
+ {item?.name -
+
{:else if selectedTab === ''} {#if item?.file?.data} diff --git a/src/lib/components/common/ImagePreview.svelte b/src/lib/components/common/ImagePreview.svelte index 17d9a93a1e..9cb36dc160 100644 --- a/src/lib/components/common/ImagePreview.svelte +++ b/src/lib/components/common/ImagePreview.svelte @@ -1,10 +1,10 @@ @@ -181,14 +160,13 @@ -
+ -
+ {/if} diff --git a/src/lib/components/common/PanzoomContainer.svelte b/src/lib/components/common/PanzoomContainer.svelte new file mode 100644 index 0000000000..50aec0709d --- /dev/null +++ b/src/lib/components/common/PanzoomContainer.svelte @@ -0,0 +1,33 @@ + + +
+ +
diff --git a/src/lib/components/common/SVGPanZoom.svelte b/src/lib/components/common/SVGPanZoom.svelte index aaeb26dac9..307ddda21e 100644 --- a/src/lib/components/common/SVGPanZoom.svelte +++ b/src/lib/components/common/SVGPanZoom.svelte @@ -4,15 +4,14 @@ import { toast } from 'svelte-sonner'; - import panzoom, { type PanZoom } from 'panzoom'; import DOMPurify from 'dompurify'; - import { onMount, getContext } from 'svelte'; + import { getContext } from 'svelte'; const i18n = getContext('i18n'); import { copyToClipboard } from '$lib/utils'; - import DocumentDuplicate from '../icons/DocumentDuplicate.svelte'; + import PanzoomContainer from './PanzoomContainer.svelte'; import Tooltip from './Tooltip.svelte'; import Clipboard from '../icons/Clipboard.svelte'; import Reset from '../icons/Reset.svelte'; @@ -22,23 +21,9 @@ export let svg = ''; export let content = ''; - let instance: PanZoom; - - let sceneParentElement: HTMLElement; - let sceneElement: HTMLElement; - - $: if (sceneElement) { - instance = panzoom(sceneElement, { - bounds: true, - boundsPadding: 0.1, - - zoomSpeed: 0.065 - }); - } + let panzoomRef: PanzoomContainer; const resetPanZoomViewport = () => { - instance.moveTo(0, 0); - instance.zoomAbs(0, 0, 1); - console.log(instance.getTransform()); + panzoomRef?.reset(); }; const downloadAsSVG = () => { @@ -47,8 +32,11 @@ }; -
-
+
+ {@html DOMPurify.sanitize(svg, { USE_PROFILES: { svg: true, svgFilters: true }, // allow , , , etc. WHOLE_DOCUMENT: false, @@ -88,7 +76,7 @@ ], SANITIZE_DOM: true })} -
+ {#if content}
From b10c70cfcf1ece2d6d9959716ec2ed66e21f602a Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Tue, 31 Mar 2026 17:11:47 +0800 Subject: [PATCH 002/334] feat: Save error messages to the database (#23231) --- backend/open_webui/utils/middleware.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index b64febd673..398d693cd5 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -3632,6 +3632,16 @@ async def streaming_chat_response_handler(response, ctx): if not choices: error = data.get('error', {}) if error: + try: + Chats.upsert_message_to_chat_by_id_and_message_id( + metadata["chat_id"], + metadata["message_id"], + { + "error": {"content": error}, + }, + ) + except Exception: + pass await event_emitter( { 'type': 'chat:completion', From 5fd9db873900f96e9822a5c5e7e368ae368c1749 Mon Sep 17 00:00:00 2001 From: Algorithm5838 <108630393+Algorithm5838@users.noreply.github.com> Date: Wed, 1 Apr 2026 01:19:47 +0300 Subject: [PATCH 003/334] perf: replace JS transition with CSS animation in CodespanToken (#23258) --- .../MarkdownInlineTokens/CodespanToken.svelte | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/lib/components/chat/Messages/Markdown/MarkdownInlineTokens/CodespanToken.svelte b/src/lib/components/chat/Messages/Markdown/MarkdownInlineTokens/CodespanToken.svelte index c0b1ec327c..1de3f13f6e 100644 --- a/src/lib/components/chat/Messages/Markdown/MarkdownInlineTokens/CodespanToken.svelte +++ b/src/lib/components/chat/Messages/Markdown/MarkdownInlineTokens/CodespanToken.svelte @@ -1,7 +1,6 @@ + + +
+ +
+ + +
+ + +
+
{$i18n.t('Instructions')}
+ +
+
+ + +
+
+ {#if event && !event.meta?.automation_id} + + {/if} +
+ +
+ + +
+
+
+ diff --git a/src/lib/components/calendar/CalendarSidebar.svelte b/src/lib/components/calendar/CalendarSidebar.svelte new file mode 100644 index 0000000000..09e604a281 --- /dev/null +++ b/src/lib/components/calendar/CalendarSidebar.svelte @@ -0,0 +1,174 @@ + + +
+ +
+
+
{miniMonthNames[miniMonth]} {miniYear}
+
+ + +
+
+ +
+ {#each ['S', 'M', 'T', 'W', 'T', 'F', 'S'] as d} +
{d}
+ {/each} +
+ +
+ {#each miniDays as day} + + {/each} +
+
+ + +
+
+
+ {$i18n.t('Calendars')} +
+
+ + {#each calendars as cal (cal.id)} + + {/each} +
+
diff --git a/src/lib/components/calendar/CalendarView.svelte b/src/lib/components/calendar/CalendarView.svelte new file mode 100644 index 0000000000..0b67d7de98 --- /dev/null +++ b/src/lib/components/calendar/CalendarView.svelte @@ -0,0 +1,380 @@ + + +
+ + + + + {#if view === 'month'} +
+
+ {#each DAY_NAMES as day} +
{$i18n.t(day)}
+ {/each} +
+ +
+ {#each monthDays as day, i} + {@const dayKey = new Date(day.getFullYear(), day.getMonth(), day.getDate()).getTime().toString()} + {@const dayEvents = eventsByDay[dayKey] || []} + {@const col = i % 7} + {@const row = Math.floor(i / 7)} + + {/each} +
+
+ + + {:else if view === 'week'} +
+
+
+
+
+
+ {#each weekDays as day} +
+
{DAY_NAMES[day.getDay()]}
+
+ {day.getDate()} +
+
+ {/each} +
+ +
+ {#each hours as hour} +
+
{hour > 0 ? formatHour(hour) : ''}
+ {#each weekDays as day} + {@const hourEvents = getEventsForHour(day, hour, filteredEvents)} + + {/each} +
+ {/each} +
+
+
+
+
+ + + {:else} +
+
+ {#each hours as hour} + {@const hourEvents = getEventsForHour(currentDate, hour, filteredEvents)} +
+
{hour > 0 ? formatHour(hour) : ''}
+ +
+ {/each} +
+
+ {/if} +
diff --git a/src/lib/components/layout/Sidebar/UserMenu.svelte b/src/lib/components/layout/Sidebar/UserMenu.svelte index 1a1681a844..30c29962b4 100644 --- a/src/lib/components/layout/Sidebar/UserMenu.svelte +++ b/src/lib/components/layout/Sidebar/UserMenu.svelte @@ -250,6 +250,38 @@ {/if} + {#if $user?.role === 'admin' || $user?.permissions?.features?.calendar} + { + if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return; + e.preventDefault(); + show = false; + goto('/calendar'); + }} + > +
+ + + +
+
{$i18n.t('Calendar')}
+
+ {/if} + {#if role === 'admin'} + import { onMount, getContext, tick } from 'svelte'; + import { toast } from 'svelte-sonner'; + import { goto } from '$app/navigation'; + import { WEBUI_NAME, mobile, showSidebar, user } from '$lib/stores'; + import { + getCalendars, + getCalendarEvents, + type CalendarModel, + type CalendarEventModel + } from '$lib/apis/calendar'; + import CalendarView from '$lib/components/calendar/CalendarView.svelte'; + import CalendarSidebar from '$lib/components/calendar/CalendarSidebar.svelte'; + import CalendarEventModal from '$lib/components/calendar/CalendarEventModal.svelte'; + import Spinner from '$lib/components/common/Spinner.svelte'; + import Plus from '$lib/components/icons/Plus.svelte'; + + const i18n = getContext('i18n'); + + let loaded = false; + let calendars: CalendarModel[] = []; + let events: CalendarEventModel[] = []; + let visibleCalendarIds: Set = new Set(); + + let view: 'month' | 'week' | 'day' = 'month'; + let currentDate = new Date(); + + let showEventModal = false; + let editEvent: CalendarEventModel | null = null; + let defaultStartAt: number | null = null; + + function getVisibleRange(): { start: string; end: string } { + const d = new Date(currentDate); + let start: Date; + let end: Date; + + if (view === 'month') { + start = new Date(d.getFullYear(), d.getMonth(), 1); + start.setDate(start.getDate() - start.getDay()); + end = new Date(start); + end.setDate(end.getDate() + 42); + } else if (view === 'week') { + start = new Date(d); + start.setDate(start.getDate() - start.getDay()); + start.setHours(0, 0, 0, 0); + end = new Date(start); + end.setDate(end.getDate() + 7); + } else { + start = new Date(d.getFullYear(), d.getMonth(), d.getDate()); + end = new Date(start); + end.setDate(end.getDate() + 1); + } + + return { + start: start.toISOString(), + end: end.toISOString() + }; + } + + async function loadCalendars() { + try { + calendars = (await getCalendars(localStorage.token)) ?? []; + visibleCalendarIds = new Set(calendars.map((c) => c.id)); + } catch (err) { + console.error('loadCalendars', err); + calendars = []; + } + } + + async function loadEvents() { + try { + const { start, end } = getVisibleRange(); + events = await getCalendarEvents(localStorage.token, start, end); + } catch (err) { + toast.error(`${err}`); + } + } + + async function refresh() { + await loadEvents(); + } + + function toggleCalendar(id: string) { + const next = new Set(visibleCalendarIds); + if (next.has(id)) { + next.delete(id); + } else { + next.add(id); + } + visibleCalendarIds = next; + } + + function handleCreateEvent(e: CustomEvent<{ start_at: number }>) { + editEvent = null; + defaultStartAt = e.detail.start_at; + showEventModal = true; + } + + function handleEventClick(e: CustomEvent) { + const evt = e.detail; + if (evt.meta?.automation_id) { + if (evt.meta?.chat_id) { + goto(`/c/${evt.meta.chat_id}`); + } else { + goto(`/automations/${evt.meta.automation_id}`); + } + return; + } + editEvent = evt; + defaultStartAt = null; + showEventModal = true; + } + + async function handleNavigate() { + await tick(); + refresh(); + } + + async function handleDateSelect(date: Date) { + currentDate = date; + await tick(); + refresh(); + } + + function handleNewEvent() { + editEvent = null; + defaultStartAt = null; + showEventModal = true; + } + + $: defaultCalendarId = calendars.find((c) => !c.is_system || c.name === 'Personal')?.id || calendars[0]?.id || ''; + + onMount(async () => { + await loadCalendars(); + await refresh(); + loaded = true; + }); + + + + {$i18n.t('Calendar')} • {$WEBUI_NAME} + + + refresh()} + on:delete={() => refresh()} +/> + +
+ {#if loaded} +
+ + + + +
+ +
+
+ {:else} +
+ +
+ {/if} +
From 4a5401b4174edbef8d102ac2917944ae1d2cdc00 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 19 Apr 2026 21:48:27 +0900 Subject: [PATCH 273/334] refac --- .../56359461a091_add_calendar_tables.py | 2 +- backend/open_webui/models/calendar.py | 40 ++++++++++++++----- backend/open_webui/routers/calendar.py | 10 ++++- src/lib/apis/calendar/index.ts | 33 ++++++++++++++- src/routes/(app)/calendar/+page.svelte | 2 +- 5 files changed, 73 insertions(+), 14 deletions(-) diff --git a/backend/open_webui/migrations/versions/56359461a091_add_calendar_tables.py b/backend/open_webui/migrations/versions/56359461a091_add_calendar_tables.py index 8277daa738..a0812578c8 100644 --- a/backend/open_webui/migrations/versions/56359461a091_add_calendar_tables.py +++ b/backend/open_webui/migrations/versions/56359461a091_add_calendar_tables.py @@ -24,7 +24,7 @@ def upgrade() -> None: sa.Column('user_id', sa.Text(), nullable=False), sa.Column('name', sa.Text(), nullable=False), sa.Column('color', sa.Text(), nullable=True), - sa.Column('is_system', sa.Boolean(), nullable=False), + sa.Column('is_default', sa.Boolean(), nullable=False), sa.Column('data', sa.JSON(), nullable=True), sa.Column('meta', sa.JSON(), nullable=True), sa.Column('created_at', sa.BigInteger(), nullable=False), diff --git a/backend/open_webui/models/calendar.py b/backend/open_webui/models/calendar.py index e055c87f90..859632c494 100644 --- a/backend/open_webui/models/calendar.py +++ b/backend/open_webui/models/calendar.py @@ -17,6 +17,7 @@ from sqlalchemy import ( exists, func, delete, + update, ) from sqlalchemy.ext.asyncio import AsyncSession @@ -40,7 +41,7 @@ class Calendar(Base): user_id = Column(Text, nullable=False) name = Column(Text, nullable=False) color = Column(Text, nullable=True) - is_system = Column(Boolean, nullable=False, default=False) + is_default = Column(Boolean, nullable=False, default=False) data = Column(JSON, nullable=True) meta = Column(JSON, nullable=True) @@ -107,7 +108,8 @@ class CalendarModel(BaseModel): user_id: str name: str color: Optional[str] = None - is_system: bool = False + is_default: bool = False + data: Optional[dict] = None meta: Optional[dict] = None @@ -269,7 +271,7 @@ class CalendarTable: user_id=user_id, name='Personal', color='#3b82f6', - is_system=True, + is_default=True, created_at=now, updated_at=now, ), @@ -278,7 +280,6 @@ class CalendarTable: user_id=user_id, name='Scheduled Tasks', color='#8b5cf6', - is_system=True, created_at=now + 1, updated_at=now + 1, ), @@ -338,7 +339,6 @@ class CalendarTable: select(Calendar).filter( Calendar.user_id == user_id, Calendar.name == 'Scheduled Tasks', - Calendar.is_system == True, ) ) cal = result.scalars().first() @@ -349,7 +349,6 @@ class CalendarTable: select(Calendar).filter( Calendar.user_id == user_id, Calendar.name == 'Scheduled Tasks', - Calendar.is_system == True, ) ) cal = result.scalars().first() @@ -366,7 +365,7 @@ class CalendarTable: user_id=user_id, name=form_data.name, color=form_data.color, - is_system=False, + is_default=False, data=form_data.data, meta=form_data.meta, created_at=now, @@ -403,13 +402,36 @@ class CalendarTable: await db.commit() return await self._to_calendar_model(cal, db=db) + async def set_default_calendar( + self, user_id: str, calendar_id: str, db: Optional[AsyncSession] = None + ) -> Optional[CalendarModel]: + """Set a calendar as the user's default, clearing all others.""" + async with get_async_db_context(db) as db: + # Clear all defaults for this user + await db.execute( + update(Calendar) + .where(Calendar.user_id == user_id, Calendar.is_default == True) + .values(is_default=False) + ) + # Set the new default + result = await db.execute( + select(Calendar).filter(Calendar.id == calendar_id, Calendar.user_id == user_id) + ) + cal = result.scalars().first() + if not cal: + return None + cal.is_default = True + cal.updated_at = int(time.time_ns()) + await db.commit() + return await self._to_calendar_model(cal, db=db) + async def delete_calendar_by_id(self, id: str, db: Optional[AsyncSession] = None) -> bool: - """Delete a non-system calendar. Cascades to events, attendees, and grants.""" + """Delete a non-default calendar. Cascades to events, attendees, and grants.""" try: async with get_async_db_context(db) as db: result = await db.execute(select(Calendar).filter(Calendar.id == id)) cal = result.scalars().first() - if not cal or cal.is_system: + if not cal or cal.is_default: return False # Delete attendees for all events in this calendar diff --git a/backend/open_webui/routers/calendar.py b/backend/open_webui/routers/calendar.py index 4b052bd754..220edf853c 100644 --- a/backend/open_webui/routers/calendar.py +++ b/backend/open_webui/routers/calendar.py @@ -310,10 +310,16 @@ async def delete_calendar(calendar_id: str, user: UserModel = Depends(get_verifi if cal.user_id != user.id and user.role != 'admin': raise HTTPException(status_code=403, detail='Only owner can delete calendar') - if cal.is_system: - raise HTTPException(status_code=400, detail='Cannot delete system calendar') result = await Calendars.delete_calendar_by_id(calendar_id) if not result: raise HTTPException(status_code=500, detail='Failed to delete') return {'status': True} + + +@router.post('/{calendar_id}/default') +async def set_default_calendar(calendar_id: str, user: UserModel = Depends(get_verified_user)): + cal = await Calendars.set_default_calendar(user.id, calendar_id) + if not cal: + raise HTTPException(status_code=404, detail='Calendar not found') + return cal diff --git a/src/lib/apis/calendar/index.ts b/src/lib/apis/calendar/index.ts index 39540bb890..fa75f9a6a7 100644 --- a/src/lib/apis/calendar/index.ts +++ b/src/lib/apis/calendar/index.ts @@ -5,7 +5,7 @@ export type CalendarModel = { user_id: string; name: string; color: string | null; - is_system: boolean; + is_default: boolean; data: Record | null; meta: Record | null; access_grants: any[]; @@ -188,6 +188,37 @@ export const deleteCalendar = async (token: string, calendarId: string): Promise return res?.status ?? false; }; +export const setDefaultCalendar = async ( + token: string, + calendarId: string +): Promise => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/calendars/${calendarId}/default`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + authorization: `Bearer ${token}` + } + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + error = err.detail; + console.error(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + // ── Events ───────────────────────────────── export const getCalendarEvents = async ( diff --git a/src/routes/(app)/calendar/+page.svelte b/src/routes/(app)/calendar/+page.svelte index 89940ef23b..25671fc791 100644 --- a/src/routes/(app)/calendar/+page.svelte +++ b/src/routes/(app)/calendar/+page.svelte @@ -128,7 +128,7 @@ showEventModal = true; } - $: defaultCalendarId = calendars.find((c) => !c.is_system || c.name === 'Personal')?.id || calendars[0]?.id || ''; + $: defaultCalendarId = calendars.find((c) => c.is_default)?.id || calendars[0]?.id || ''; onMount(async () => { await loadCalendars(); From f0ec5ee08ff6978131b3d657c8a8bc98c8533d05 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 19 Apr 2026 21:49:48 +0900 Subject: [PATCH 274/334] refac --- src/lib/components/calendar/CalendarSidebar.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/components/calendar/CalendarSidebar.svelte b/src/lib/components/calendar/CalendarSidebar.svelte index 09e604a281..8ed0df4727 100644 --- a/src/lib/components/calendar/CalendarSidebar.svelte +++ b/src/lib/components/calendar/CalendarSidebar.svelte @@ -72,7 +72,7 @@
-
{miniMonthNames[miniMonth]} {miniYear}
+
{miniMonthNames[miniMonth]} {miniYear}
-
+
{#each ['S', 'M', 'T', 'W', 'T', 'F', 'S'] as d}
{d}
{/each}
-
+
{#each miniDays as day}
+ + diff --git a/src/lib/components/workspace/Models/BuiltinTools.svelte b/src/lib/components/workspace/Models/BuiltinTools.svelte index b900d004b3..fe99005688 100644 --- a/src/lib/components/workspace/Models/BuiltinTools.svelte +++ b/src/lib/components/workspace/Models/BuiltinTools.svelte @@ -50,6 +50,10 @@ automations: { label: $i18n.t('Automations'), description: $i18n.t('Create and manage scheduled automations') + }, + calendar: { + label: $i18n.t('Calendar'), + description: $i18n.t('List calendars, search, create, update, and delete calendar events') } }; From f45d0f130ef9c3d3958f54320b987f2e2eacc421 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 19 Apr 2026 22:22:15 +0900 Subject: [PATCH 276/334] refac --- backend/open_webui/config.py | 6 +++ backend/open_webui/main.py | 3 ++ backend/open_webui/routers/auths.py | 4 ++ backend/open_webui/routers/calendar.py | 49 ++++++++++++++----- backend/open_webui/utils/tools.py | 5 +- .../components/admin/Settings/General.svelte | 8 +++ .../components/layout/Sidebar/UserMenu.svelte | 2 +- 7 files changed, 63 insertions(+), 14 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index a68720a7c0..1ee107a6ac 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1624,6 +1624,12 @@ ENABLE_CHANNELS = PersistentConfig( os.environ.get('ENABLE_CHANNELS', 'False').lower() == 'true', ) +ENABLE_CALENDAR = PersistentConfig( + 'ENABLE_CALENDAR', + 'calendar.enable', + os.environ.get('ENABLE_CALENDAR', 'True').lower() == 'true', +) + AUTOMATION_MAX_COUNT = PersistentConfig( 'AUTOMATION_MAX_COUNT', 'automations.max_count', diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index c13250c587..23379a7750 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -396,6 +396,7 @@ from open_webui.config import ( AUTOMATION_MAX_COUNT, AUTOMATION_MIN_INTERVAL, ENABLE_CHANNELS, + ENABLE_CALENDAR, ENABLE_NOTES, ENABLE_USER_STATUS, ENABLE_COMMUNITY_SHARING, @@ -902,6 +903,7 @@ app.state.config.FOLDER_MAX_FILE_COUNT = FOLDER_MAX_FILE_COUNT app.state.config.AUTOMATION_MAX_COUNT = AUTOMATION_MAX_COUNT app.state.config.AUTOMATION_MIN_INTERVAL = AUTOMATION_MIN_INTERVAL app.state.config.ENABLE_CHANNELS = ENABLE_CHANNELS +app.state.config.ENABLE_CALENDAR = ENABLE_CALENDAR app.state.config.ENABLE_NOTES = ENABLE_NOTES app.state.config.ENABLE_COMMUNITY_SHARING = ENABLE_COMMUNITY_SHARING app.state.config.ENABLE_MESSAGE_RATING = ENABLE_MESSAGE_RATING @@ -2218,6 +2220,7 @@ async def get_app_config(request: Request): 'enable_folders': app.state.config.ENABLE_FOLDERS, 'folder_max_file_count': app.state.config.FOLDER_MAX_FILE_COUNT, 'enable_channels': app.state.config.ENABLE_CHANNELS, + 'enable_calendar': app.state.config.ENABLE_CALENDAR, 'enable_notes': app.state.config.ENABLE_NOTES, 'enable_web_search': app.state.config.ENABLE_WEB_SEARCH, 'enable_code_execution': app.state.config.ENABLE_CODE_EXECUTION, diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index 651e123b64..c8daa4957a 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -972,6 +972,7 @@ async def get_admin_config(request: Request, user=Depends(get_admin_user)): 'AUTOMATION_MAX_COUNT': request.app.state.config.AUTOMATION_MAX_COUNT, 'AUTOMATION_MIN_INTERVAL': request.app.state.config.AUTOMATION_MIN_INTERVAL, 'ENABLE_CHANNELS': request.app.state.config.ENABLE_CHANNELS, + 'ENABLE_CALENDAR': request.app.state.config.ENABLE_CALENDAR, 'ENABLE_MEMORIES': request.app.state.config.ENABLE_MEMORIES, 'ENABLE_NOTES': request.app.state.config.ENABLE_NOTES, 'ENABLE_USER_WEBHOOKS': request.app.state.config.ENABLE_USER_WEBHOOKS, @@ -1000,6 +1001,7 @@ class AdminConfig(BaseModel): AUTOMATION_MAX_COUNT: Optional[int | str] = None AUTOMATION_MIN_INTERVAL: Optional[int | str] = None ENABLE_CHANNELS: bool + ENABLE_CALENDAR: bool ENABLE_MEMORIES: bool ENABLE_NOTES: bool ENABLE_USER_WEBHOOKS: bool @@ -1031,6 +1033,7 @@ async def update_admin_config(request: Request, form_data: AdminConfig, user=Dep int(form_data.AUTOMATION_MIN_INTERVAL) if form_data.AUTOMATION_MIN_INTERVAL else '' ) request.app.state.config.ENABLE_CHANNELS = form_data.ENABLE_CHANNELS + request.app.state.config.ENABLE_CALENDAR = form_data.ENABLE_CALENDAR request.app.state.config.ENABLE_MEMORIES = form_data.ENABLE_MEMORIES request.app.state.config.ENABLE_NOTES = form_data.ENABLE_NOTES @@ -1074,6 +1077,7 @@ async def update_admin_config(request: Request, form_data: AdminConfig, user=Dep 'AUTOMATION_MAX_COUNT': request.app.state.config.AUTOMATION_MAX_COUNT, 'AUTOMATION_MIN_INTERVAL': request.app.state.config.AUTOMATION_MIN_INTERVAL, 'ENABLE_CHANNELS': request.app.state.config.ENABLE_CHANNELS, + 'ENABLE_CALENDAR': request.app.state.config.ENABLE_CALENDAR, 'ENABLE_MEMORIES': request.app.state.config.ENABLE_MEMORIES, 'ENABLE_NOTES': request.app.state.config.ENABLE_NOTES, 'ENABLE_USER_WEBHOOKS': request.app.state.config.ENABLE_USER_WEBHOOKS, diff --git a/backend/open_webui/routers/calendar.py b/backend/open_webui/routers/calendar.py index 220edf853c..f92f5b8943 100644 --- a/backend/open_webui/routers/calendar.py +++ b/backend/open_webui/routers/calendar.py @@ -2,7 +2,7 @@ import logging import time from typing import Optional -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, Depends, HTTPException, Request from pydantic import BaseModel from open_webui.models.calendar import ( @@ -24,12 +24,22 @@ from open_webui.models.groups import Groups from open_webui.models.users import UserModel from open_webui.utils.auth import get_verified_user from open_webui.utils.calendar import expand_recurring_event +from open_webui.constants import ERROR_MESSAGES log = logging.getLogger(__name__) router = APIRouter() +async def check_calendar_enabled(request: Request): + """Dependency to ensure calendar feature is globally enabled.""" + if not request.app.state.config.ENABLE_CALENDAR: + raise HTTPException( + status_code=403, + detail=ERROR_MESSAGES.FEATURE_DISABLED('Calendar'), + ) + + async def _check_calendar_access( calendar_id: str, user: UserModel, permission: str = 'write' ) -> CalendarModel: @@ -58,14 +68,16 @@ async def _check_calendar_access( @router.get('/', response_model=list[CalendarModel]) -async def get_calendars(user: UserModel = Depends(get_verified_user)): +async def get_calendars(request: Request, user: UserModel = Depends(get_verified_user)): """List user's calendars (owned + shared). Auto-creates defaults on first call.""" + await check_calendar_enabled(request) return await Calendars.get_calendars_by_user(user.id) @router.post('/create', response_model=CalendarModel) -async def create_calendar(form_data: CalendarForm, user: UserModel = Depends(get_verified_user)): +async def create_calendar(request: Request, form_data: CalendarForm, user: UserModel = Depends(get_verified_user)): """Create a new user calendar.""" + await check_calendar_enabled(request) return await Calendars.insert_new_calendar(user.id, form_data) @@ -76,6 +88,7 @@ async def create_calendar(form_data: CalendarForm, user: UserModel = Depends(get @router.get('/events') async def get_events( + request: Request, start: str, end: str, calendar_ids: Optional[str] = None, @@ -92,6 +105,7 @@ async def get_events( - Stored events from the database - Virtual events computed from active automation RRULEs (Scheduled Tasks calendar) """ + await check_calendar_enabled(request) from datetime import datetime try: @@ -203,25 +217,29 @@ async def get_events( @router.post('/events/create', response_model=CalendarEventModel) -async def create_event(form_data: CalendarEventForm, user: UserModel = Depends(get_verified_user)): +async def create_event(request: Request, form_data: CalendarEventForm, user: UserModel = Depends(get_verified_user)): + await check_calendar_enabled(request) await _check_calendar_access(form_data.calendar_id, user, 'write') return await CalendarEvents.insert_new_event(user.id, form_data) @router.get('/events/search', response_model=CalendarEventListResponse) async def search_events( + request: Request, query: Optional[str] = None, skip: int = 0, limit: int = 30, user: UserModel = Depends(get_verified_user), ): + await check_calendar_enabled(request) return await CalendarEvents.search_events( user_id=user.id, query=query, skip=skip, limit=limit ) @router.get('/events/{event_id}', response_model=CalendarEventModel) -async def get_event(event_id: str, user: UserModel = Depends(get_verified_user)): +async def get_event(request: Request, event_id: str, user: UserModel = Depends(get_verified_user)): + await check_calendar_enabled(request) event = await CalendarEvents.get_event_by_id(event_id) if not event: raise HTTPException(status_code=404, detail='Event not found') @@ -233,8 +251,9 @@ async def get_event(event_id: str, user: UserModel = Depends(get_verified_user)) @router.post('/events/{event_id}/update', response_model=CalendarEventModel) async def update_event( - event_id: str, form_data: CalendarEventUpdateForm, user: UserModel = Depends(get_verified_user) + request: Request, event_id: str, form_data: CalendarEventUpdateForm, user: UserModel = Depends(get_verified_user) ): + await check_calendar_enabled(request) event = await CalendarEvents.get_event_by_id(event_id) if not event: raise HTTPException(status_code=404, detail='Event not found') @@ -248,7 +267,8 @@ async def update_event( @router.delete('/events/{event_id}/delete') -async def delete_event(event_id: str, user: UserModel = Depends(get_verified_user)): +async def delete_event(request: Request, event_id: str, user: UserModel = Depends(get_verified_user)): + await check_calendar_enabled(request) event = await CalendarEvents.get_event_by_id(event_id) if not event: raise HTTPException(status_code=404, detail='Event not found') @@ -263,9 +283,10 @@ async def delete_event(event_id: str, user: UserModel = Depends(get_verified_use @router.post('/events/{event_id}/rsvp', response_model=dict) async def rsvp_event( - event_id: str, form_data: RSVPForm, user: UserModel = Depends(get_verified_user) + request: Request, event_id: str, form_data: RSVPForm, user: UserModel = Depends(get_verified_user) ): """Update own RSVP status for an event.""" + await check_calendar_enabled(request) if form_data.status not in ('accepted', 'declined', 'tentative', 'pending'): raise HTTPException(status_code=400, detail='Invalid status') @@ -281,15 +302,17 @@ async def rsvp_event( @router.get('/{calendar_id}', response_model=CalendarModel) -async def get_calendar_by_id(calendar_id: str, user: UserModel = Depends(get_verified_user)): +async def get_calendar_by_id(request: Request, calendar_id: str, user: UserModel = Depends(get_verified_user)): + await check_calendar_enabled(request) cal = await _check_calendar_access(calendar_id, user, 'read') return cal @router.post('/{calendar_id}/update', response_model=CalendarModel) async def update_calendar( - calendar_id: str, form_data: CalendarUpdateForm, user: UserModel = Depends(get_verified_user) + request: Request, calendar_id: str, form_data: CalendarUpdateForm, user: UserModel = Depends(get_verified_user) ): + await check_calendar_enabled(request) cal = await _check_calendar_access(calendar_id, user, 'write') # Only owner/admin can change access grants @@ -303,7 +326,8 @@ async def update_calendar( @router.delete('/{calendar_id}/delete') -async def delete_calendar(calendar_id: str, user: UserModel = Depends(get_verified_user)): +async def delete_calendar(request: Request, calendar_id: str, user: UserModel = Depends(get_verified_user)): + await check_calendar_enabled(request) cal = await _check_calendar_access(calendar_id, user, 'write') # Only owner/admin can delete @@ -318,7 +342,8 @@ async def delete_calendar(calendar_id: str, user: UserModel = Depends(get_verifi @router.post('/{calendar_id}/default') -async def set_default_calendar(calendar_id: str, user: UserModel = Depends(get_verified_user)): +async def set_default_calendar(request: Request, calendar_id: str, user: UserModel = Depends(get_verified_user)): + await check_calendar_enabled(request) cal = await Calendars.set_default_calendar(user.id, calendar_id) if not cal: raise HTTPException(status_code=404, detail='Calendar not found') diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index 52a8868391..e0791a35ff 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -557,7 +557,10 @@ async def get_builtin_tools( ) # Calendar tools - search/create/update/delete events - if is_builtin_tool_enabled('calendar'): + if ( + is_builtin_tool_enabled('calendar') + and getattr(request.app.state.config, 'ENABLE_CALENDAR', False) + ): builtin_functions.extend( [search_calendar_events, create_calendar_event, update_calendar_event, delete_calendar_event] ) diff --git a/src/lib/components/admin/Settings/General.svelte b/src/lib/components/admin/Settings/General.svelte index f2ba4a3ee1..f535bd68ee 100644 --- a/src/lib/components/admin/Settings/General.svelte +++ b/src/lib/components/admin/Settings/General.svelte @@ -756,6 +756,14 @@
+
+
+ {$i18n.t('Calendar')} ({$i18n.t('Beta')}) +
+ + +
+
{$i18n.t('Memories')} ({$i18n.t('Beta')}) diff --git a/src/lib/components/layout/Sidebar/UserMenu.svelte b/src/lib/components/layout/Sidebar/UserMenu.svelte index 30c29962b4..b159a0dd61 100644 --- a/src/lib/components/layout/Sidebar/UserMenu.svelte +++ b/src/lib/components/layout/Sidebar/UserMenu.svelte @@ -250,7 +250,7 @@ {/if} - {#if $user?.role === 'admin' || $user?.permissions?.features?.calendar} + {#if $config?.features?.enable_calendar && ($user?.role === 'admin' || $user?.permissions?.features?.calendar)} Date: Sun, 19 Apr 2026 22:33:32 +0900 Subject: [PATCH 277/334] refac --- backend/open_webui/config.py | 6 ++++++ backend/open_webui/main.py | 3 +++ backend/open_webui/routers/auths.py | 4 ++++ backend/open_webui/routers/automations.py | 5 +++++ backend/open_webui/utils/automations.py | 4 ++++ backend/open_webui/utils/tools.py | 6 +++++- src/lib/components/admin/Settings/General.svelte | 14 +++++++++++--- src/lib/components/layout/Sidebar/UserMenu.svelte | 2 +- src/routes/(app)/automations/+page.svelte | 2 +- src/routes/(app)/automations/[id]/+page.svelte | 4 ++-- 10 files changed, 42 insertions(+), 8 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 1ee107a6ac..53de67387f 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1630,6 +1630,12 @@ ENABLE_CALENDAR = PersistentConfig( os.environ.get('ENABLE_CALENDAR', 'True').lower() == 'true', ) +ENABLE_AUTOMATIONS = PersistentConfig( + 'ENABLE_AUTOMATIONS', + 'automations.enable', + os.environ.get('ENABLE_AUTOMATIONS', 'True').lower() == 'true', +) + AUTOMATION_MAX_COUNT = PersistentConfig( 'AUTOMATION_MAX_COUNT', 'automations.max_count', diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 23379a7750..f9b21e6592 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -393,6 +393,7 @@ from open_webui.config import ( API_KEYS_ALLOWED_ENDPOINTS, ENABLE_FOLDERS, FOLDER_MAX_FILE_COUNT, + ENABLE_AUTOMATIONS, AUTOMATION_MAX_COUNT, AUTOMATION_MIN_INTERVAL, ENABLE_CHANNELS, @@ -900,6 +901,7 @@ app.state.config.BANNERS = WEBUI_BANNERS app.state.config.ENABLE_FOLDERS = ENABLE_FOLDERS app.state.config.FOLDER_MAX_FILE_COUNT = FOLDER_MAX_FILE_COUNT +app.state.config.ENABLE_AUTOMATIONS = ENABLE_AUTOMATIONS app.state.config.AUTOMATION_MAX_COUNT = AUTOMATION_MAX_COUNT app.state.config.AUTOMATION_MIN_INTERVAL = AUTOMATION_MIN_INTERVAL app.state.config.ENABLE_CHANNELS = ENABLE_CHANNELS @@ -2221,6 +2223,7 @@ async def get_app_config(request: Request): 'folder_max_file_count': app.state.config.FOLDER_MAX_FILE_COUNT, 'enable_channels': app.state.config.ENABLE_CHANNELS, 'enable_calendar': app.state.config.ENABLE_CALENDAR, + 'enable_automations': app.state.config.ENABLE_AUTOMATIONS, 'enable_notes': app.state.config.ENABLE_NOTES, 'enable_web_search': app.state.config.ENABLE_WEB_SEARCH, 'enable_code_execution': app.state.config.ENABLE_CODE_EXECUTION, diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index c8daa4957a..d3337d8109 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -971,6 +971,7 @@ async def get_admin_config(request: Request, user=Depends(get_admin_user)): 'FOLDER_MAX_FILE_COUNT': request.app.state.config.FOLDER_MAX_FILE_COUNT, 'AUTOMATION_MAX_COUNT': request.app.state.config.AUTOMATION_MAX_COUNT, 'AUTOMATION_MIN_INTERVAL': request.app.state.config.AUTOMATION_MIN_INTERVAL, + 'ENABLE_AUTOMATIONS': request.app.state.config.ENABLE_AUTOMATIONS, 'ENABLE_CHANNELS': request.app.state.config.ENABLE_CHANNELS, 'ENABLE_CALENDAR': request.app.state.config.ENABLE_CALENDAR, 'ENABLE_MEMORIES': request.app.state.config.ENABLE_MEMORIES, @@ -1000,6 +1001,7 @@ class AdminConfig(BaseModel): FOLDER_MAX_FILE_COUNT: Optional[int | str] = None AUTOMATION_MAX_COUNT: Optional[int | str] = None AUTOMATION_MIN_INTERVAL: Optional[int | str] = None + ENABLE_AUTOMATIONS: bool ENABLE_CHANNELS: bool ENABLE_CALENDAR: bool ENABLE_MEMORIES: bool @@ -1032,6 +1034,7 @@ async def update_admin_config(request: Request, form_data: AdminConfig, user=Dep request.app.state.config.AUTOMATION_MIN_INTERVAL = ( int(form_data.AUTOMATION_MIN_INTERVAL) if form_data.AUTOMATION_MIN_INTERVAL else '' ) + request.app.state.config.ENABLE_AUTOMATIONS = form_data.ENABLE_AUTOMATIONS request.app.state.config.ENABLE_CHANNELS = form_data.ENABLE_CHANNELS request.app.state.config.ENABLE_CALENDAR = form_data.ENABLE_CALENDAR request.app.state.config.ENABLE_MEMORIES = form_data.ENABLE_MEMORIES @@ -1076,6 +1079,7 @@ async def update_admin_config(request: Request, form_data: AdminConfig, user=Dep 'FOLDER_MAX_FILE_COUNT': request.app.state.config.FOLDER_MAX_FILE_COUNT, 'AUTOMATION_MAX_COUNT': request.app.state.config.AUTOMATION_MAX_COUNT, 'AUTOMATION_MIN_INTERVAL': request.app.state.config.AUTOMATION_MIN_INTERVAL, + 'ENABLE_AUTOMATIONS': request.app.state.config.ENABLE_AUTOMATIONS, 'ENABLE_CHANNELS': request.app.state.config.ENABLE_CHANNELS, 'ENABLE_CALENDAR': request.app.state.config.ENABLE_CALENDAR, 'ENABLE_MEMORIES': request.app.state.config.ENABLE_MEMORIES, diff --git a/backend/open_webui/routers/automations.py b/backend/open_webui/routers/automations.py index d68bd8e2c6..ed33c4e8cb 100644 --- a/backend/open_webui/routers/automations.py +++ b/backend/open_webui/routers/automations.py @@ -39,6 +39,11 @@ PAGE_ITEM_COUNT = 30 async def check_automations_permission(request, user): + if not request.app.state.config.ENABLE_AUTOMATIONS: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=ERROR_MESSAGES.UNAUTHORIZED, + ) if user.role != 'admin' and not await has_permission( user.id, 'features.automations', request.app.state.config.USER_PERMISSIONS ): diff --git a/backend/open_webui/utils/automations.py b/backend/open_webui/utils/automations.py index 3866eb865a..ac1f4df699 100644 --- a/backend/open_webui/utils/automations.py +++ b/backend/open_webui/utils/automations.py @@ -126,6 +126,10 @@ async def automation_worker_loop(app) -> None: log.info(f'Automation worker started (poll interval: {AUTOMATION_POLL_INTERVAL}s)') while True: try: + if not getattr(app.state.config, 'ENABLE_AUTOMATIONS', False): + await asyncio.sleep(AUTOMATION_POLL_INTERVAL) + continue + async with get_async_db() as db: batch = await Automations.claim_due(int(time.time_ns()), limit=10, db=db) if batch: diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index e0791a35ff..1c47fc75a6 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -551,7 +551,11 @@ async def get_builtin_tools( builtin_functions.extend([create_tasks, update_task]) # Automation tools - create and manage scheduled automations from chat - if is_builtin_tool_enabled('automations') and await has_user_permission('automations'): + if ( + is_builtin_tool_enabled('automations') + and getattr(request.app.state.config, 'ENABLE_AUTOMATIONS', False) + and await has_user_permission('automations') + ): builtin_functions.extend( [create_automation, update_automation, list_automations, toggle_automation, delete_automation] ) diff --git a/src/lib/components/admin/Settings/General.svelte b/src/lib/components/admin/Settings/General.svelte index f535bd68ee..ddb81e844b 100644 --- a/src/lib/components/admin/Settings/General.svelte +++ b/src/lib/components/admin/Settings/General.svelte @@ -740,6 +740,14 @@
{/if} +
+
+ {$i18n.t('Memories')} ({$i18n.t('Beta')}) +
+ + +
+
{$i18n.t('Notes')} ({$i18n.t('Beta')}) @@ -758,7 +766,7 @@
- {$i18n.t('Calendar')} ({$i18n.t('Beta')}) + {$i18n.t('Calendar')}
@@ -766,10 +774,10 @@
- {$i18n.t('Memories')} ({$i18n.t('Beta')}) + {$i18n.t('Automations')}
- +
diff --git a/src/lib/components/layout/Sidebar/UserMenu.svelte b/src/lib/components/layout/Sidebar/UserMenu.svelte index b159a0dd61..dddcf42550 100644 --- a/src/lib/components/layout/Sidebar/UserMenu.svelte +++ b/src/lib/components/layout/Sidebar/UserMenu.svelte @@ -214,7 +214,7 @@
{$i18n.t('Settings')}
- {#if $user?.role === 'admin' || $user?.permissions?.features?.automations} + {#if $config?.features?.enable_automations && ($user?.role === 'admin' || $user?.permissions?.features?.automations)}
{ - if ($user?.role !== 'admin' && !($user?.permissions?.features?.automations ?? false)) { + if (!$config?.features?.enable_automations || ($user?.role !== 'admin' && !($user?.permissions?.features?.automations ?? false))) { goto('/'); return; } diff --git a/src/routes/(app)/automations/[id]/+page.svelte b/src/routes/(app)/automations/[id]/+page.svelte index 51745fa01e..9d7cf6bbed 100644 --- a/src/routes/(app)/automations/[id]/+page.svelte +++ b/src/routes/(app)/automations/[id]/+page.svelte @@ -4,7 +4,7 @@ import { onMount, getContext } from 'svelte'; import { page } from '$app/stores'; - import { user, showSidebar } from '$lib/stores'; + import { user, showSidebar, config } from '$lib/stores'; import { getAutomationById } from '$lib/apis/automations'; import AutomationEditor from '$lib/components/automations/AutomationEditor.svelte'; @@ -18,7 +18,7 @@ $: automationId = $page.params.id; onMount(async () => { - if ($user?.role !== 'admin' && !($user?.permissions?.features?.automations ?? false)) { + if (!$config?.features?.enable_automations || ($user?.role !== 'admin' && !($user?.permissions?.features?.automations ?? false))) { goto('/'); return; } From 5afc258c5b13f456be528420513ade546c5e86f9 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 19 Apr 2026 22:37:10 +0900 Subject: [PATCH 278/334] refac --- backend/open_webui/config.py | 5 ++ backend/open_webui/routers/calendar.py | 45 ++++++++++-------- backend/open_webui/utils/tools.py | 1 + .../admin/Users/Groups/Permissions.svelte | 16 +++++++ static/favicon.png | Bin 10655 -> 21666 bytes static/static/favicon.png | Bin 10655 -> 21666 bytes 6 files changed, 48 insertions(+), 19 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 53de67387f..c43ead1d79 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1524,6 +1524,10 @@ USER_PERMISSIONS_FEATURES_AUTOMATIONS = ( os.environ.get('USER_PERMISSIONS_FEATURES_AUTOMATIONS', 'False').lower() == 'true' ) +USER_PERMISSIONS_FEATURES_CALENDAR = ( + os.environ.get('USER_PERMISSIONS_FEATURES_CALENDAR', 'True').lower() == 'true' +) + USER_PERMISSIONS_SETTINGS_INTERFACE = os.environ.get('USER_PERMISSIONS_SETTINGS_INTERFACE', 'True').lower() == 'true' @@ -1594,6 +1598,7 @@ DEFAULT_USER_PERMISSIONS = { 'code_interpreter': USER_PERMISSIONS_FEATURES_CODE_INTERPRETER, 'memories': USER_PERMISSIONS_FEATURES_MEMORIES, 'automations': USER_PERMISSIONS_FEATURES_AUTOMATIONS, + 'calendar': USER_PERMISSIONS_FEATURES_CALENDAR, }, 'settings': { 'interface': USER_PERMISSIONS_SETTINGS_INTERFACE, diff --git a/backend/open_webui/routers/calendar.py b/backend/open_webui/routers/calendar.py index f92f5b8943..5fb7cb6f9d 100644 --- a/backend/open_webui/routers/calendar.py +++ b/backend/open_webui/routers/calendar.py @@ -2,8 +2,7 @@ import logging import time from typing import Optional -from fastapi import APIRouter, Depends, HTTPException, Request -from pydantic import BaseModel +from fastapi import APIRouter, Depends, HTTPException, Request, status from open_webui.models.calendar import ( Calendars, @@ -23,6 +22,7 @@ from open_webui.models.access_grants import AccessGrants from open_webui.models.groups import Groups from open_webui.models.users import UserModel from open_webui.utils.auth import get_verified_user +from open_webui.utils.access_control import has_permission from open_webui.utils.calendar import expand_recurring_event from open_webui.constants import ERROR_MESSAGES @@ -31,12 +31,19 @@ log = logging.getLogger(__name__) router = APIRouter() -async def check_calendar_enabled(request: Request): - """Dependency to ensure calendar feature is globally enabled.""" +async def check_calendar_permission(request: Request, user): + """Check global feature flag AND per-user permission for calendar access.""" if not request.app.state.config.ENABLE_CALENDAR: raise HTTPException( - status_code=403, - detail=ERROR_MESSAGES.FEATURE_DISABLED('Calendar'), + status_code=status.HTTP_403_FORBIDDEN, + detail=ERROR_MESSAGES.UNAUTHORIZED, + ) + if user.role != 'admin' and not await has_permission( + user.id, 'features.calendar', request.app.state.config.USER_PERMISSIONS + ): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=ERROR_MESSAGES.UNAUTHORIZED, ) @@ -70,14 +77,14 @@ async def _check_calendar_access( @router.get('/', response_model=list[CalendarModel]) async def get_calendars(request: Request, user: UserModel = Depends(get_verified_user)): """List user's calendars (owned + shared). Auto-creates defaults on first call.""" - await check_calendar_enabled(request) + await check_calendar_permission(request, user) return await Calendars.get_calendars_by_user(user.id) @router.post('/create', response_model=CalendarModel) async def create_calendar(request: Request, form_data: CalendarForm, user: UserModel = Depends(get_verified_user)): """Create a new user calendar.""" - await check_calendar_enabled(request) + await check_calendar_permission(request, user) return await Calendars.insert_new_calendar(user.id, form_data) @@ -105,7 +112,7 @@ async def get_events( - Stored events from the database - Virtual events computed from active automation RRULEs (Scheduled Tasks calendar) """ - await check_calendar_enabled(request) + await check_calendar_permission(request, user) from datetime import datetime try: @@ -218,7 +225,7 @@ async def get_events( @router.post('/events/create', response_model=CalendarEventModel) async def create_event(request: Request, form_data: CalendarEventForm, user: UserModel = Depends(get_verified_user)): - await check_calendar_enabled(request) + await check_calendar_permission(request, user) await _check_calendar_access(form_data.calendar_id, user, 'write') return await CalendarEvents.insert_new_event(user.id, form_data) @@ -231,7 +238,7 @@ async def search_events( limit: int = 30, user: UserModel = Depends(get_verified_user), ): - await check_calendar_enabled(request) + await check_calendar_permission(request, user) return await CalendarEvents.search_events( user_id=user.id, query=query, skip=skip, limit=limit ) @@ -239,7 +246,7 @@ async def search_events( @router.get('/events/{event_id}', response_model=CalendarEventModel) async def get_event(request: Request, event_id: str, user: UserModel = Depends(get_verified_user)): - await check_calendar_enabled(request) + await check_calendar_permission(request, user) event = await CalendarEvents.get_event_by_id(event_id) if not event: raise HTTPException(status_code=404, detail='Event not found') @@ -253,7 +260,7 @@ async def get_event(request: Request, event_id: str, user: UserModel = Depends(g async def update_event( request: Request, event_id: str, form_data: CalendarEventUpdateForm, user: UserModel = Depends(get_verified_user) ): - await check_calendar_enabled(request) + await check_calendar_permission(request, user) event = await CalendarEvents.get_event_by_id(event_id) if not event: raise HTTPException(status_code=404, detail='Event not found') @@ -268,7 +275,7 @@ async def update_event( @router.delete('/events/{event_id}/delete') async def delete_event(request: Request, event_id: str, user: UserModel = Depends(get_verified_user)): - await check_calendar_enabled(request) + await check_calendar_permission(request, user) event = await CalendarEvents.get_event_by_id(event_id) if not event: raise HTTPException(status_code=404, detail='Event not found') @@ -286,7 +293,7 @@ async def rsvp_event( request: Request, event_id: str, form_data: RSVPForm, user: UserModel = Depends(get_verified_user) ): """Update own RSVP status for an event.""" - await check_calendar_enabled(request) + await check_calendar_permission(request, user) if form_data.status not in ('accepted', 'declined', 'tentative', 'pending'): raise HTTPException(status_code=400, detail='Invalid status') @@ -303,7 +310,7 @@ async def rsvp_event( @router.get('/{calendar_id}', response_model=CalendarModel) async def get_calendar_by_id(request: Request, calendar_id: str, user: UserModel = Depends(get_verified_user)): - await check_calendar_enabled(request) + await check_calendar_permission(request, user) cal = await _check_calendar_access(calendar_id, user, 'read') return cal @@ -312,7 +319,7 @@ async def get_calendar_by_id(request: Request, calendar_id: str, user: UserModel async def update_calendar( request: Request, calendar_id: str, form_data: CalendarUpdateForm, user: UserModel = Depends(get_verified_user) ): - await check_calendar_enabled(request) + await check_calendar_permission(request, user) cal = await _check_calendar_access(calendar_id, user, 'write') # Only owner/admin can change access grants @@ -327,7 +334,7 @@ async def update_calendar( @router.delete('/{calendar_id}/delete') async def delete_calendar(request: Request, calendar_id: str, user: UserModel = Depends(get_verified_user)): - await check_calendar_enabled(request) + await check_calendar_permission(request, user) cal = await _check_calendar_access(calendar_id, user, 'write') # Only owner/admin can delete @@ -343,7 +350,7 @@ async def delete_calendar(request: Request, calendar_id: str, user: UserModel = @router.post('/{calendar_id}/default') async def set_default_calendar(request: Request, calendar_id: str, user: UserModel = Depends(get_verified_user)): - await check_calendar_enabled(request) + await check_calendar_permission(request, user) cal = await Calendars.set_default_calendar(user.id, calendar_id) if not cal: raise HTTPException(status_code=404, detail='Calendar not found') diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index 1c47fc75a6..471ec8540d 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -564,6 +564,7 @@ async def get_builtin_tools( if ( is_builtin_tool_enabled('calendar') and getattr(request.app.state.config, 'ENABLE_CALENDAR', False) + and await has_user_permission('calendar') ): builtin_functions.extend( [search_calendar_events, create_calendar_event, update_calendar_event, delete_calendar_event] diff --git a/src/lib/components/admin/Users/Groups/Permissions.svelte b/src/lib/components/admin/Users/Groups/Permissions.svelte index 7bd8fd00e0..cbfcb67b0a 100644 --- a/src/lib/components/admin/Users/Groups/Permissions.svelte +++ b/src/lib/components/admin/Users/Groups/Permissions.svelte @@ -916,6 +916,22 @@
{/if}
+ +
+
+
+ {$i18n.t('Calendar')} +
+ +
+ {#if defaultPermissions?.features?.calendar && !permissions.features.calendar} +
+
+ {$i18n.t('This is a default user permission and will remain enabled.')} +
+
+ {/if} +

diff --git a/static/favicon.png b/static/favicon.png index 63735ad4616fa452325af0fe351139dca01ca0ab..10c84f440ced21353ee824440758cbd080c7bf55 100644 GIT binary patch literal 21666 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 literal 10655 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 literal 10655 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 Date: Sun, 19 Apr 2026 22:40:59 +0900 Subject: [PATCH 279/334] refac --- static/static/favicon.ico | Bin 15086 -> 4286 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/static/static/favicon.ico b/static/static/favicon.ico index 14c5f9c6d437ed109a8579031cf181ee52bdf30e..b819d42f96d1b745d1d815521c0997d588f451ef 100644 GIT binary patch literal 4286 zcmeHJ%S)6|6u;vrp+z%U447HcLfS-#2u0qH`)ow`v`laeqb zaa{Q*a>xf^O^#0j!MBL-7d{%b)9+m8`}i8C8Fju|d57Q3opbIvzjMz$-}$Z(27DqT z1%HcoW+5y>hwpC&d<-c6c-oYDlIL&eHN6Il-w#P zD6k|XB!qy&;EhMonM@`@M53yy>gM3!;ERQY1?&3yx_xtVb8KsC>&IEZHn9B;AFV?} zLwPkdHR0^rY(^1y7~$dJlDn&`>;CHM>c^v_qeHCMWw5WcwY5(+o9zJ{qE;hMM9|aI zBih>9ZXoV7xzP>;BBO)T?-WL}avk>cn2UA@{oQI{QrRQ)aqN1YI z7%D3(MN(2y!0ztu8(oOJIN96Vdz+V+XRNQUSNZLByKp!hAx@{$=EYPO)xp6*_wexW z71X!NKQS>OCMPFNhlhv#x{!Er0uKB1^z=3Gsr*w@Q(|Uj#>_e|rn;!WF)%wj8wx&^ zAMr5%HDCPm^Yc_!S4TKMLSLfB#ztCMSyAI!US5`R^d8#Z-&f_@2{;_?{2U8p%>P}x zRy*(Q?WNe*STYz45(u7*Mk6IBC)3#2m|Ti;q_D6sii?Y*?d@%?xjH`&Kk`E{F)RdC)%*>RzZf`%?8qwEfY(+&y#CbYJ{s-pf=0b7aYW=y+pZD*T zzoVl=6V>swwzgLC7Zw)&Gybx&GRcvXlSAl{Y7BT!5uZKRq19^rXZc5epy=pmS$|ns zS(2-%sfp6l(`Ef}4Qy;|oVNaC{yFE$@RfhwaCdjNdjcL$tP!K5qiX(n0y*O1s>)X~oT_<&_QdK!xU5otLr_R}HuYa#S4~~=J zWIEluI~wLW|LNp7|8yKDFE8{v$8m1!yBrPqKB=bTEYJx5&^W5%{Hoynw-6D@R5Vny zQ4CZpR~%H_RJ>GFgupZJ__jq)1fIL62RR_Pr{j(y~0tdLn zRSp`D`cAo}((h{CBXEIJmF+>}&~8#u-_>kPfm5$obxFU|N7DBHt^&8HeXD+>@BJQq zWU~H&+i!klpzj%1zvbAJEa%F4aP*AR`a46x_?<3NqD0;Kl0GwkDpMpRZ{NO^YuB#H z@#Dv3TNkY15_|%$_}4?%ur{4~H_1 z^b_?+L*~HD0TY^wUAlBhCQh6vZQ8VvtgI{{DT^94YDi8_jtm<%Otx;_DxW|54(&BK%Q;$DuUyIi?sJu@>?YS*qUwQAM!%@bu+xg4s=AI@+esEA7r zA3iLtTD9`&a0QKLp?d_XAx{&25| zNg(tQyWw^D_3PL1sF^aFIdi6~OB>j+V~5cVfIr;Roqt^QhyF0!|7B%maryt%t5-5) z$PibTay)qOU{L;tdtB{zK#pwU; z+_~fFKijr#E9cIgi>{zwDk>_ZYuBzWck+!NtOQX1;m#OPr7(W=@891ue)aC%+th`R zA3u8Z;eGY$RT(^Zu!lQyi(|+BTHXb~pRqTt`2&4D^|nQe79LJbnlv#!U9ez*Y~Q|J zii(P4{P^+GxpQYvUhzHi>u?ExKiu0W9)+hH?V0)8vSrJB=WjJ@)->Z3{bP7Lx6h+S zjS}?3rJDTV40qj>6Dtmjyh+I_JKeFpR+cCF2N)vlXZdsOTGjC^q% zc9-P6WtBb z{QNt~EXaiME9JNHE+-7jPv!htKUlxm(E82wqxGxxvqr@4+|$Za{96&kJuR&d%@z5I zBE_GI$BNGi7Qa06j&H>{+@SyBhE7gIzJ~%LUn&o{I1}D ziG2Fm)Yra3Ty)SSjUN)>q4DX1E-B~(6S1q&J%TQ2kd`@Dtcr)m>!Y}@fPYXzTBO-0 zmVNzBzKKUU1}&sX+P;3!_mnq&3NqiCoJU9-8xi$E-%(G37&kSUn1YSp!^=T`)5fT) z)v0T$9+zMPTW;IbXWD8^zdw8SOm5t`Av<^Olx^F#$;FEo(D{5i*@9Jo`FfPHLY+qdjF1Lh1K`=zC&5qAVE;memVo4t4LdQ6xw zAyNCZ1K)O6**<3ve!@N{d(MeQ)G{l8|-8smTJ;Fc|063suh z|5QZOKkfi^@7~?yi+Zqc-#&A$Ykff5#7AAabTMT_$XR{@)b_{yBW%}>_kWoIQJ19zA*#(Jm7mf3$rw2BmcT4RJ`>{3B#PrSq>4hm_4f zv5#n7=%Yx<{QD~d!vQ~}Wc?Awf%=e=^_S&9s2uG2lm3D}i+GE(MsS z+Xui&P^?~QD4lXmleDpcEo@?&eTp1Ko+6Qb3eDp$ibBOT1#23>bD`oFP0m;JTdv{{ z#Y+YLqs*+>^5YwEa>Enhx8i?__X@u{ps$VajX1=0)6i$qQ4PndZG_a7KX&zw19?r4x^NYKHj z*A&KAoMUtEi8;sl^XJW3e7A1h%>6O=a2JI6b4buZ_k(@GJrM3b*}DhKGscY@C;j^M zGx@2cj~coj{h>pLy86t;NV9L>zOnS-;Niby$r5v)yQHK<;Lkmk82k%W-}ukY`|%(5 zjoRy7%*Bfrn>APr|3#D^KBla=3u))q(aJAW2a>KoA^mFA->@{2YCq^(QD53RNx4wA bfagL*MEiZNd%>mb_i9fBsuCLy9d!Q>dBe1} From 37eba1c5a66b3145c122a6b40e5c29707526d121 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 19 Apr 2026 22:45:54 +0900 Subject: [PATCH 280/334] chore: format --- backend/open_webui/config.py | 4 +- .../56359461a091_add_calendar_tables.py | 82 ++++----- backend/open_webui/models/automations.py | 13 +- backend/open_webui/models/calendar.py | 98 ++++------- backend/open_webui/routers/calendar.py | 67 ++++--- backend/open_webui/routers/knowledge.py | 4 +- backend/open_webui/static/favicon.ico | Bin 15086 -> 4286 bytes backend/open_webui/static/favicon.png | Bin 10655 -> 21666 bytes backend/open_webui/tools/builtin.py | 13 +- backend/open_webui/utils/middleware.py | 30 +++- src/lib/apis/calendar/index.ts | 1 - .../calendar/CalendarEventChip.svelte | 6 +- .../calendar/CalendarEventModal.svelte | 6 +- .../components/calendar/CalendarView.svelte | 165 +++++++++++++++--- src/lib/components/chat/Chat.svelte | 2 +- .../chat/Messages/ResponseMessage.svelte | 2 +- src/lib/i18n/locales/ar-BH/translation.json | 16 ++ src/lib/i18n/locales/ar/translation.json | 16 ++ src/lib/i18n/locales/az-AZ/translation.json | 16 ++ src/lib/i18n/locales/bg-BG/translation.json | 16 ++ src/lib/i18n/locales/bn-BD/translation.json | 16 ++ src/lib/i18n/locales/bo-TB/translation.json | 16 ++ src/lib/i18n/locales/bs-BA/translation.json | 16 ++ src/lib/i18n/locales/ca-ES/translation.json | 16 ++ src/lib/i18n/locales/ceb-PH/translation.json | 16 ++ src/lib/i18n/locales/cs-CZ/translation.json | 16 ++ src/lib/i18n/locales/da-DK/translation.json | 16 ++ src/lib/i18n/locales/de-DE/translation.json | 16 ++ src/lib/i18n/locales/dg-DG/translation.json | 16 ++ src/lib/i18n/locales/el-GR/translation.json | 16 ++ src/lib/i18n/locales/en-GB/translation.json | 16 ++ src/lib/i18n/locales/en-US/translation.json | 16 ++ src/lib/i18n/locales/es-ES/translation.json | 16 ++ src/lib/i18n/locales/et-EE/translation.json | 16 ++ src/lib/i18n/locales/eu-ES/translation.json | 16 ++ src/lib/i18n/locales/fa-IR/translation.json | 16 ++ src/lib/i18n/locales/fi-FI/translation.json | 16 ++ src/lib/i18n/locales/fr-CA/translation.json | 16 ++ src/lib/i18n/locales/fr-FR/translation.json | 16 ++ src/lib/i18n/locales/gl-ES/translation.json | 16 ++ src/lib/i18n/locales/he-IL/translation.json | 16 ++ src/lib/i18n/locales/hi-IN/translation.json | 16 ++ src/lib/i18n/locales/hr-HR/translation.json | 16 ++ src/lib/i18n/locales/hu-HU/translation.json | 16 ++ src/lib/i18n/locales/id-ID/translation.json | 16 ++ src/lib/i18n/locales/ie-GA/translation.json | 16 ++ src/lib/i18n/locales/it-IT/translation.json | 16 ++ src/lib/i18n/locales/ja-JP/translation.json | 16 ++ src/lib/i18n/locales/ka-GE/translation.json | 16 ++ src/lib/i18n/locales/kab-DZ/translation.json | 16 ++ src/lib/i18n/locales/ko-KR/translation.json | 16 ++ src/lib/i18n/locales/lt-LT/translation.json | 16 ++ src/lib/i18n/locales/lv-LV/translation.json | 16 ++ src/lib/i18n/locales/ms-MY/translation.json | 16 ++ src/lib/i18n/locales/nb-NO/translation.json | 16 ++ src/lib/i18n/locales/nl-NL/translation.json | 16 ++ src/lib/i18n/locales/pa-IN/translation.json | 16 ++ src/lib/i18n/locales/pl-PL/translation.json | 16 ++ src/lib/i18n/locales/pt-BR/translation.json | 16 ++ src/lib/i18n/locales/pt-PT/translation.json | 16 ++ src/lib/i18n/locales/ro-RO/translation.json | 16 ++ src/lib/i18n/locales/ru-RU/translation.json | 16 ++ src/lib/i18n/locales/sk-SK/translation.json | 16 ++ src/lib/i18n/locales/sr-RS/translation.json | 16 ++ src/lib/i18n/locales/sv-SE/translation.json | 16 ++ src/lib/i18n/locales/ta-IN/translation.json | 16 ++ src/lib/i18n/locales/th-TH/translation.json | 16 ++ src/lib/i18n/locales/tk-TM/translation.json | 16 ++ src/lib/i18n/locales/tr-TR/translation.json | 16 ++ src/lib/i18n/locales/ug-CN/translation.json | 16 ++ src/lib/i18n/locales/uk-UA/translation.json | 16 ++ src/lib/i18n/locales/ur-PK/translation.json | 16 ++ .../i18n/locales/uz-Cyrl-UZ/translation.json | 16 ++ .../i18n/locales/uz-Latn-Uz/translation.json | 16 ++ src/lib/i18n/locales/vi-VN/translation.json | 16 ++ src/lib/i18n/locales/zh-CN/translation.json | 16 ++ src/lib/i18n/locales/zh-TW/translation.json | 16 ++ src/routes/(app)/automations/+page.svelte | 5 +- .../(app)/automations/[id]/+page.svelte | 5 +- 79 files changed, 1272 insertions(+), 207 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index c43ead1d79..d2c88cb2fb 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1524,9 +1524,7 @@ USER_PERMISSIONS_FEATURES_AUTOMATIONS = ( os.environ.get('USER_PERMISSIONS_FEATURES_AUTOMATIONS', 'False').lower() == 'true' ) -USER_PERMISSIONS_FEATURES_CALENDAR = ( - os.environ.get('USER_PERMISSIONS_FEATURES_CALENDAR', 'True').lower() == 'true' -) +USER_PERMISSIONS_FEATURES_CALENDAR = os.environ.get('USER_PERMISSIONS_FEATURES_CALENDAR', 'True').lower() == 'true' USER_PERMISSIONS_SETTINGS_INTERFACE = os.environ.get('USER_PERMISSIONS_SETTINGS_INTERFACE', 'True').lower() == 'true' diff --git a/backend/open_webui/migrations/versions/56359461a091_add_calendar_tables.py b/backend/open_webui/migrations/versions/56359461a091_add_calendar_tables.py index a0812578c8..e556440f56 100644 --- a/backend/open_webui/migrations/versions/56359461a091_add_calendar_tables.py +++ b/backend/open_webui/migrations/versions/56359461a091_add_calendar_tables.py @@ -5,6 +5,7 @@ Revises: c1d2e3f4a5b6 Create Date: 2026-04-19 16:20:58.162045 """ + from typing import Sequence, Union from alembic import op @@ -19,52 +20,55 @@ depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: - op.create_table('calendar', - sa.Column('id', sa.Text(), nullable=False), - sa.Column('user_id', sa.Text(), nullable=False), - sa.Column('name', sa.Text(), nullable=False), - sa.Column('color', sa.Text(), nullable=True), - sa.Column('is_default', sa.Boolean(), nullable=False), - sa.Column('data', sa.JSON(), nullable=True), - sa.Column('meta', sa.JSON(), nullable=True), - sa.Column('created_at', sa.BigInteger(), nullable=False), - sa.Column('updated_at', sa.BigInteger(), nullable=False), - sa.PrimaryKeyConstraint('id') + op.create_table( + 'calendar', + sa.Column('id', sa.Text(), nullable=False), + sa.Column('user_id', sa.Text(), nullable=False), + sa.Column('name', sa.Text(), nullable=False), + sa.Column('color', sa.Text(), nullable=True), + sa.Column('is_default', sa.Boolean(), nullable=False), + sa.Column('data', sa.JSON(), nullable=True), + sa.Column('meta', sa.JSON(), nullable=True), + sa.Column('created_at', sa.BigInteger(), nullable=False), + sa.Column('updated_at', sa.BigInteger(), nullable=False), + sa.PrimaryKeyConstraint('id'), ) op.create_index('ix_calendar_user', 'calendar', ['user_id'], unique=False) - op.create_table('calendar_event', - sa.Column('id', sa.Text(), nullable=False), - sa.Column('calendar_id', sa.Text(), nullable=False), - sa.Column('user_id', sa.Text(), nullable=False), - sa.Column('title', sa.Text(), nullable=False), - sa.Column('description', sa.Text(), nullable=True), - sa.Column('start_at', sa.BigInteger(), nullable=False), - sa.Column('end_at', sa.BigInteger(), nullable=True), - sa.Column('all_day', sa.Boolean(), nullable=False), - sa.Column('rrule', sa.Text(), nullable=True), - sa.Column('color', sa.Text(), nullable=True), - sa.Column('location', sa.Text(), nullable=True), - sa.Column('data', sa.JSON(), nullable=True), - sa.Column('meta', sa.JSON(), nullable=True), - sa.Column('is_cancelled', sa.Boolean(), nullable=False), - sa.Column('created_at', sa.BigInteger(), nullable=False), - sa.Column('updated_at', sa.BigInteger(), nullable=False), - sa.PrimaryKeyConstraint('id') + op.create_table( + 'calendar_event', + sa.Column('id', sa.Text(), nullable=False), + sa.Column('calendar_id', sa.Text(), nullable=False), + sa.Column('user_id', sa.Text(), nullable=False), + sa.Column('title', sa.Text(), nullable=False), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('start_at', sa.BigInteger(), nullable=False), + sa.Column('end_at', sa.BigInteger(), nullable=True), + sa.Column('all_day', sa.Boolean(), nullable=False), + sa.Column('rrule', sa.Text(), nullable=True), + sa.Column('color', sa.Text(), nullable=True), + sa.Column('location', sa.Text(), nullable=True), + sa.Column('data', sa.JSON(), nullable=True), + sa.Column('meta', sa.JSON(), nullable=True), + sa.Column('is_cancelled', sa.Boolean(), nullable=False), + sa.Column('created_at', sa.BigInteger(), nullable=False), + sa.Column('updated_at', sa.BigInteger(), nullable=False), + sa.PrimaryKeyConstraint('id'), ) op.create_index('ix_calendar_event_calendar', 'calendar_event', ['calendar_id', 'start_at'], unique=False) op.create_index('ix_calendar_event_user_date', 'calendar_event', ['user_id', 'start_at'], unique=False) - op.create_table('calendar_event_attendee', - sa.Column('id', sa.Text(), nullable=False), - sa.Column('event_id', sa.Text(), nullable=False), - sa.Column('user_id', sa.Text(), nullable=False), - sa.Column('status', sa.Text(), nullable=False), - sa.Column('meta', sa.JSON(), nullable=True), - sa.Column('created_at', sa.BigInteger(), nullable=False), - sa.Column('updated_at', sa.BigInteger(), nullable=False), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('event_id', 'user_id', name='uq_event_attendee') + op.create_table( + 'calendar_event_attendee', + sa.Column('id', sa.Text(), nullable=False), + sa.Column('event_id', sa.Text(), nullable=False), + sa.Column('user_id', sa.Text(), nullable=False), + sa.Column('status', sa.Text(), nullable=False), + sa.Column('meta', sa.JSON(), nullable=True), + sa.Column('created_at', sa.BigInteger(), nullable=False), + sa.Column('updated_at', sa.BigInteger(), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('event_id', 'user_id', name='uq_event_attendee'), ) op.create_index('ix_calendar_event_attendee_user', 'calendar_event_attendee', ['user_id', 'status'], unique=False) diff --git a/backend/open_webui/models/automations.py b/backend/open_webui/models/automations.py index c7c78a7c8e..05f449ad13 100644 --- a/backend/open_webui/models/automations.py +++ b/backend/open_webui/models/automations.py @@ -153,15 +153,11 @@ class AutomationTable: row = await db.get(Automation, id) return AutomationModel.model_validate(row) if row else None - async def get_active_by_user( - self, user_id: str, db: Optional[AsyncSession] = None - ) -> list[AutomationModel]: + async def get_active_by_user(self, user_id: str, db: Optional[AsyncSession] = None) -> list[AutomationModel]: """Get active automations for a user (for calendar RRULE expansion).""" async with get_async_db_context(db) as db: result = await db.execute( - select(Automation) - .filter_by(user_id=user_id, is_active=True) - .order_by(Automation.created_at.desc()) + select(Automation).filter_by(user_id=user_id, is_active=True).order_by(Automation.created_at.desc()) ) return [AutomationModel.model_validate(r) for r in result.scalars().all()] @@ -291,9 +287,8 @@ class AutomationTable: timezone_by_user_id: dict[str, Optional[str]] = {} if user_ids: from open_webui.models.users import User - tz_result = await db.execute( - select(User.id, User.timezone).where(User.id.in_(user_ids)) - ) + + tz_result = await db.execute(select(User.id, User.timezone).where(User.id.in_(user_ids))) timezone_by_user_id = {uid: tz for uid, tz in tz_result.all()} for row in rows: diff --git a/backend/open_webui/models/calendar.py b/backend/open_webui/models/calendar.py index 859632c494..9d2d71a45b 100644 --- a/backend/open_webui/models/calendar.py +++ b/backend/open_webui/models/calendar.py @@ -232,9 +232,7 @@ class CalendarEventListResponse(BaseModel): class CalendarTable: - async def _get_access_grants( - self, calendar_id: str, db: Optional[AsyncSession] = None - ) -> list[AccessGrantModel]: + async def _get_access_grants(self, calendar_id: str, db: Optional[AsyncSession] = None) -> list[AccessGrantModel]: return await AccessGrants.get_grants_by_resource('calendar', calendar_id, db=db) async def _to_calendar_model( @@ -245,15 +243,11 @@ class CalendarTable: ) -> CalendarModel: cal_data = CalendarModel.model_validate(cal).model_dump(exclude={'access_grants'}) cal_data['access_grants'] = ( - access_grants - if access_grants is not None - else await self._get_access_grants(cal_data['id'], db=db) + access_grants if access_grants is not None else await self._get_access_grants(cal_data['id'], db=db) ) return CalendarModel.model_validate(cal_data) - async def get_or_create_defaults( - self, user_id: str, db: Optional[AsyncSession] = None - ) -> list[CalendarModel]: + async def get_or_create_defaults(self, user_id: str, db: Optional[AsyncSession] = None) -> list[CalendarModel]: """Return user's calendars, creating 'Personal' and 'Scheduled Tasks' if none exist.""" async with get_async_db_context(db) as db: result = await db.execute( @@ -289,9 +283,7 @@ class CalendarTable: await db.commit() return [CalendarModel.model_validate(c) for c in defaults] - async def get_calendars_by_user( - self, user_id: str, db: Optional[AsyncSession] = None - ) -> list[CalendarModel]: + async def get_calendars_by_user(self, user_id: str, db: Optional[AsyncSession] = None) -> list[CalendarModel]: """Owned + shared calendars.""" async with get_async_db_context(db) as db: user_groups = await Groups.get_groups_by_member_id(user_id, db=db) @@ -317,14 +309,9 @@ class CalendarTable: cal_ids = [c.id for c in calendars] grants_map = await AccessGrants.get_grants_by_resources('calendar', cal_ids, db=db) - return [ - await self._to_calendar_model(c, access_grants=grants_map.get(c.id, []), db=db) - for c in calendars - ] + return [await self._to_calendar_model(c, access_grants=grants_map.get(c.id, []), db=db) for c in calendars] - async def get_calendar_by_id( - self, id: str, db: Optional[AsyncSession] = None - ) -> Optional[CalendarModel]: + async def get_calendar_by_id(self, id: str, db: Optional[AsyncSession] = None) -> Optional[CalendarModel]: async with get_async_db_context(db) as db: result = await db.execute(select(Calendar).filter(Calendar.id == id)) cal = result.scalars().first() @@ -414,9 +401,7 @@ class CalendarTable: .values(is_default=False) ) # Set the new default - result = await db.execute( - select(Calendar).filter(Calendar.id == calendar_id, Calendar.user_id == user_id) - ) + result = await db.execute(select(Calendar).filter(Calendar.id == calendar_id, Calendar.user_id == user_id)) cal = result.scalars().first() if not cal: return None @@ -435,15 +420,11 @@ class CalendarTable: return False # Delete attendees for all events in this calendar - event_ids_result = await db.execute( - select(CalendarEvent.id).filter(CalendarEvent.calendar_id == id) - ) + event_ids_result = await db.execute(select(CalendarEvent.id).filter(CalendarEvent.calendar_id == id)) event_ids = [r[0] for r in event_ids_result.all()] if event_ids: await db.execute( - delete(CalendarEventAttendee).filter( - CalendarEventAttendee.event_id.in_(event_ids) - ) + delete(CalendarEventAttendee).filter(CalendarEventAttendee.event_id.in_(event_ids)) ) # Delete events @@ -465,9 +446,7 @@ class CalendarEventTable: self, event_id: str, db: Optional[AsyncSession] = None ) -> list[CalendarEventAttendeeModel]: async with get_async_db_context(db) as db: - result = await db.execute( - select(CalendarEventAttendee).filter(CalendarEventAttendee.event_id == event_id) - ) + result = await db.execute(select(CalendarEventAttendee).filter(CalendarEventAttendee.event_id == event_id)) rows = result.scalars().all() return [CalendarEventAttendeeModel.model_validate(r) for r in rows] @@ -515,9 +494,7 @@ class CalendarEventTable: return await self._to_event_model(event, db=db) - async def get_event_by_id( - self, id: str, db: Optional[AsyncSession] = None - ) -> Optional[CalendarEventModel]: + async def get_event_by_id(self, id: str, db: Optional[AsyncSession] = None) -> Optional[CalendarEventModel]: async with get_async_db_context(db) as db: result = await db.execute(select(CalendarEvent).filter(CalendarEvent.id == id)) event = result.scalars().first() @@ -559,9 +536,7 @@ class CalendarEventTable: # Also get event IDs where user is an attendee attendee_event_ids_result = await db.execute( - select(CalendarEventAttendee.event_id).filter( - CalendarEventAttendee.user_id == user_id - ) + select(CalendarEventAttendee.event_id).filter(CalendarEventAttendee.user_id == user_id) ) attendee_event_ids = [r[0] for r in attendee_event_ids_result.all()] @@ -608,16 +583,12 @@ class CalendarEventTable: # Batch-load attendees for all events in one query (avoid N+1) event_ids = [event.id for event, _user in items] att_result = await db.execute( - select(CalendarEventAttendee).filter( - CalendarEventAttendee.event_id.in_(event_ids) - ) + select(CalendarEventAttendee).filter(CalendarEventAttendee.event_id.in_(event_ids)) ) att_rows = att_result.scalars().all() att_map: dict[str, list[CalendarEventAttendeeModel]] = {} for a in att_rows: - att_map.setdefault(a.event_id, []).append( - CalendarEventAttendeeModel.model_validate(a) - ) + att_map.setdefault(a.event_id, []).append(CalendarEventAttendeeModel.model_validate(a)) events = [] for event, user in items: @@ -697,16 +668,12 @@ class CalendarEventTable: # Batch-load attendees event_ids = [event.id for event, _user in items] att_result = await db.execute( - select(CalendarEventAttendee).filter( - CalendarEventAttendee.event_id.in_(event_ids) - ) + select(CalendarEventAttendee).filter(CalendarEventAttendee.event_id.in_(event_ids)) ) att_rows = att_result.scalars().all() att_map: dict[str, list[CalendarEventAttendeeModel]] = {} for a in att_rows: - att_map.setdefault(a.event_id, []).append( - CalendarEventAttendeeModel.model_validate(a) - ) + att_map.setdefault(a.event_id, []).append(CalendarEventAttendeeModel.model_validate(a)) events = [] for event, user in items: @@ -732,8 +699,16 @@ class CalendarEventTable: update_data = form_data.model_dump(exclude_unset=True) for field in [ - 'calendar_id', 'title', 'description', 'start_at', 'end_at', - 'all_day', 'rrule', 'color', 'location', 'is_cancelled', + 'calendar_id', + 'title', + 'description', + 'start_at', + 'end_at', + 'all_day', + 'rrule', + 'color', + 'location', + 'is_cancelled', ]: if field in update_data: setattr(event, field, update_data[field]) @@ -750,13 +725,10 @@ class CalendarEventTable: await db.commit() return await self._to_event_model(event, db=db) - async def delete_event_by_id(self, id: str, db: Optional[AsyncSession] = None) -> bool: try: async with get_async_db_context(db) as db: - await db.execute( - delete(CalendarEventAttendee).filter(CalendarEventAttendee.event_id == id) - ) + await db.execute(delete(CalendarEventAttendee).filter(CalendarEventAttendee.event_id == id)) await db.execute(delete(CalendarEvent).filter(CalendarEvent.id == id)) await db.commit() return True @@ -774,9 +746,7 @@ class CalendarEventAttendeeTable: """ async with get_async_db_context(db) as db: # Remove existing - await db.execute( - delete(CalendarEventAttendee).filter(CalendarEventAttendee.event_id == event_id) - ) + await db.execute(delete(CalendarEventAttendee).filter(CalendarEventAttendee.event_id == event_id)) now = int(time.time_ns()) models = [] @@ -819,20 +789,14 @@ class CalendarEventAttendeeTable: self, event_id: str, db: Optional[AsyncSession] = None ) -> list[CalendarEventAttendeeModel]: async with get_async_db_context(db) as db: - result = await db.execute( - select(CalendarEventAttendee).filter(CalendarEventAttendee.event_id == event_id) - ) + result = await db.execute(select(CalendarEventAttendee).filter(CalendarEventAttendee.event_id == event_id)) return [CalendarEventAttendeeModel.model_validate(r) for r in result.scalars().all()] - async def get_events_by_attendee( - self, user_id: str, db: Optional[AsyncSession] = None - ) -> list[str]: + async def get_events_by_attendee(self, user_id: str, db: Optional[AsyncSession] = None) -> list[str]: """Return event IDs where user is an attendee.""" async with get_async_db_context(db) as db: result = await db.execute( - select(CalendarEventAttendee.event_id).filter( - CalendarEventAttendee.user_id == user_id - ) + select(CalendarEventAttendee.event_id).filter(CalendarEventAttendee.user_id == user_id) ) return [r[0] for r in result.all()] diff --git a/backend/open_webui/routers/calendar.py b/backend/open_webui/routers/calendar.py index 5fb7cb6f9d..47093ca788 100644 --- a/backend/open_webui/routers/calendar.py +++ b/backend/open_webui/routers/calendar.py @@ -47,9 +47,7 @@ async def check_calendar_permission(request: Request, user): ) -async def _check_calendar_access( - calendar_id: str, user: UserModel, permission: str = 'write' -) -> CalendarModel: +async def _check_calendar_access(calendar_id: str, user: UserModel, permission: str = 'write') -> CalendarModel: """Verify user has access to a calendar. Returns the calendar or raises 403/404.""" cal = await Calendars.get_calendar_by_id(calendar_id) if not cal: @@ -139,9 +137,7 @@ async def get_events( for event in events: event_dict = event.model_dump() if event_dict.get('rrule'): - instances = expand_recurring_event( - event_dict, start_ns, end_ns, tz=user.timezone - ) + instances = expand_recurring_event(event_dict, start_ns, end_ns, tz=user.timezone) for inst in instances: expanded.append(CalendarEventUserResponse(**{**inst, 'user': event.user})) else: @@ -189,34 +185,34 @@ async def get_events( expanded.append(CalendarEventUserResponse(**inst)) # Past runs: single range query joined with automation - runs_with_auto = await AutomationRuns.get_runs_by_user_range( - user.id, start_ns, end_ns - ) + runs_with_auto = await AutomationRuns.get_runs_by_user_range(user.id, start_ns, end_ns) for run, auto in runs_with_auto: - expanded.append(CalendarEventUserResponse( - id=f'run_{run.id}', - calendar_id=scheduled_cal.id, - user_id=user.id, - title=auto.name, - description=run.error if run.status == 'error' else '', - start_at=run.created_at, - end_at=None, - all_day=False, - color=None, - location=None, - data=None, - meta={ - 'automation_id': auto.id, - 'run_id': run.id, - 'chat_id': run.chat_id, - 'status': run.status, - }, - is_cancelled=False, - attendees=[], - created_at=run.created_at, - updated_at=run.created_at, - user=None, - )) + expanded.append( + CalendarEventUserResponse( + id=f'run_{run.id}', + calendar_id=scheduled_cal.id, + user_id=user.id, + title=auto.name, + description=run.error if run.status == 'error' else '', + start_at=run.created_at, + end_at=None, + all_day=False, + color=None, + location=None, + data=None, + meta={ + 'automation_id': auto.id, + 'run_id': run.id, + 'chat_id': run.chat_id, + 'status': run.status, + }, + is_cancelled=False, + attendees=[], + created_at=run.created_at, + updated_at=run.created_at, + user=None, + ) + ) except Exception as e: log.warning(f'Failed to compute automation events: {e}', exc_info=True) @@ -239,9 +235,7 @@ async def search_events( user: UserModel = Depends(get_verified_user), ): await check_calendar_permission(request, user) - return await CalendarEvents.search_events( - user_id=user.id, query=query, skip=skip, limit=limit - ) + return await CalendarEvents.search_events(user_id=user.id, query=query, skip=skip, limit=limit) @router.get('/events/{event_id}', response_model=CalendarEventModel) @@ -341,7 +335,6 @@ async def delete_calendar(request: Request, calendar_id: str, user: UserModel = if cal.user_id != user.id and user.role != 'admin': raise HTTPException(status_code=403, detail='Only owner can delete calendar') - result = await Calendars.delete_calendar_by_id(calendar_id) if not result: raise HTTPException(status_code=500, detail='Failed to delete') diff --git a/backend/open_webui/routers/knowledge.py b/backend/open_webui/routers/knowledge.py index d8d92b2428..f503169fc0 100644 --- a/backend/open_webui/routers/knowledge.py +++ b/backend/open_webui/routers/knowledge.py @@ -539,9 +539,7 @@ async def update_knowledge_access_by_id( 'sharing.public_knowledge', ) - knowledge.access_grants = await AccessGrants.set_access_grants( - 'knowledge', id, form_data.access_grants, db=db - ) + knowledge.access_grants = await AccessGrants.set_access_grants('knowledge', id, form_data.access_grants, db=db) return KnowledgeFilesResponse( **knowledge.model_dump(), diff --git a/backend/open_webui/static/favicon.ico b/backend/open_webui/static/favicon.ico index 14c5f9c6d437ed109a8579031cf181ee52bdf30e..b819d42f96d1b745d1d815521c0997d588f451ef 100644 GIT binary patch literal 4286 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 bfagL*MEiZNd%>mb_i9fBsuCLy9d!Q>dBe1} diff --git a/backend/open_webui/static/favicon.png b/backend/open_webui/static/favicon.png index 63735ad4616fa452325af0fe351139dca01ca0ab..10c84f440ced21353ee824440758cbd080c7bf55 100644 GIT binary patch literal 21666 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 literal 10655 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 dict: } - - - async def search_calendar_events( query: Optional[str] = None, start: Optional[str] = None, @@ -2915,7 +2912,11 @@ async def search_calendar_events( return json.dumps({'error': f'Invalid start datetime: {e}'}) try: - end_ns = _dt_to_ns(end, tz) if end else int(time.time() * 1_000) * 1_000_000 + 365 * 86400 * 1_000_000_000_000 + end_ns = ( + _dt_to_ns(end, tz) + if end + else int(time.time() * 1_000) * 1_000_000 + 365 * 86400 * 1_000_000_000_000 + ) except (ValueError, TypeError) as e: return json.dumps({'error': f'Invalid end datetime: {e}'}) @@ -2929,7 +2930,8 @@ async def search_calendar_events( if query: q = query.lower() items = [ - e for e in items + e + for e in items if q in (e.title or '').lower() or q in (e.description or '').lower() or q in (e.location or '').lower() @@ -3229,4 +3231,3 @@ async def delete_calendar_event( except Exception as e: log.exception(f'delete_calendar_event error: {e}') return json.dumps({'error': str(e)}) - diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index d44112b4ec..036dc9bf39 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -427,7 +427,11 @@ def _render_openai_tool_call_handler(item: dict, done: bool) -> str: if atype == 'search': queries = action.get('queries') or [] query = action.get('query', '') - summary = f'Search: {", ".join(str(q) for q in queries)}' if queries else (f'Search: {query}' if query else '') + summary = ( + f'Search: {", ".join(str(q) for q in queries)}' + if queries + else (f'Search: {query}' if query else '') + ) elif atype == 'open_page': summary = f'Open page: {action.get("url", "")}' if action.get('url') else '' elif atype == 'find_in_page': @@ -490,9 +494,13 @@ def serialize_output(output: list) -> str: files = result_item.get('files') embeds = result_item.get('embeds', '') - parts.append(f'
\nTool Executed\n{html.escape(json.dumps(result_text, ensure_ascii=False))}\n
') + parts.append( + f'
\nTool Executed\n{html.escape(json.dumps(result_text, ensure_ascii=False))}\n
' + ) else: - parts.append(f'
\nExecuting...\n
') + parts.append( + f'
\nExecuting...\n
' + ) elif item_type == 'function_call_output': # Already handled inline with function_call above @@ -529,9 +537,13 @@ def serialize_output(output: list) -> str: ) if status == 'completed' or duration is not None or not is_last_item: - parts.append(f'
\nThought for {duration or 0} seconds\n{display}\n
') + parts.append( + f'
\nThought for {duration or 0} seconds\n{display}\n
' + ) else: - parts.append(f'
\nThinking…\n{display}\n
') + parts.append( + f'
\nThinking…\n{display}\n
' + ) elif item_type == 'open_webui:code_interpreter': # Code interpreter needs to inspect/mutate prior accumulated content @@ -570,9 +582,13 @@ def serialize_output(output: list) -> str: output_attr = f' output="{html.escape(output_json)}"' if status == 'completed' or duration is not None or not is_last_item: - parts.append(f'
\nAnalyzed\n{display}\n
') + parts.append( + f'
\nAnalyzed\n{display}\n
' + ) else: - parts.append(f'
\nAnalyzing…\n{display}\n
') + parts.append( + f'
\nAnalyzing…\n{display}\n
' + ) return '\n'.join(parts).strip() diff --git a/src/lib/apis/calendar/index.ts b/src/lib/apis/calendar/index.ts index fa75f9a6a7..b496a42bc2 100644 --- a/src/lib/apis/calendar/index.ts +++ b/src/lib/apis/calendar/index.ts @@ -418,7 +418,6 @@ export const rsvpCalendarEvent = async ( return res; }; - export const searchCalendarEvents = async ( token: string, query: string | null, diff --git a/src/lib/components/calendar/CalendarEventChip.svelte b/src/lib/components/calendar/CalendarEventChip.svelte index c63eac1063..b1c3552e6c 100644 --- a/src/lib/components/calendar/CalendarEventChip.svelte +++ b/src/lib/components/calendar/CalendarEventChip.svelte @@ -21,7 +21,11 @@ style="background-color: {event.color || calendarColor || '#3b82f6'};" > - {#if !event.all_day}{new Date(event.start_at / 1_000_000).toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' }).replace(' ', '')}{/if} + {#if !event.all_day}{new Date(event.start_at / 1_000_000) + .toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' }) + .replace(' ', '')}{/if} {event.title} diff --git a/src/lib/components/calendar/CalendarEventModal.svelte b/src/lib/components/calendar/CalendarEventModal.svelte index 5540bda2fe..020a89f923 100644 --- a/src/lib/components/calendar/CalendarEventModal.svelte +++ b/src/lib/components/calendar/CalendarEventModal.svelte @@ -8,7 +8,11 @@ import Spinner from '$lib/components/common/Spinner.svelte'; import type { CalendarModel, CalendarEventModel, CalendarEventForm } from '$lib/apis/calendar'; - import { createCalendarEvent, updateCalendarEvent, deleteCalendarEvent } from '$lib/apis/calendar'; + import { + createCalendarEvent, + updateCalendarEvent, + deleteCalendarEvent + } from '$lib/apis/calendar'; const i18n = getContext('i18n'); const dispatch = createEventDispatcher(); diff --git a/src/lib/components/calendar/CalendarView.svelte b/src/lib/components/calendar/CalendarView.svelte index 0b67d7de98..5100a58b94 100644 --- a/src/lib/components/calendar/CalendarView.svelte +++ b/src/lib/components/calendar/CalendarView.svelte @@ -21,11 +21,24 @@ const NS = 1_000_000; const DAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const MONTH_NAMES = [ - 'January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', 'November', 'December' + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' ]; - $: calColorMap = calendars.reduce((acc, c) => ({ ...acc, [c.id]: c.color }), {} as Record); + $: calColorMap = calendars.reduce( + (acc, c) => ({ ...acc, [c.id]: c.color }), + {} as Record + ); $: filteredEvents = events.filter((e) => visibleCalendarIds.has(e.calendar_id)); // Pre-group events by day key so the template reactively updates when events change @@ -102,7 +115,11 @@ }); } - function getEventsForHour(day: Date, hour: number, eventsList: CalendarEventModel[] = filteredEvents): CalendarEventModel[] { + function getEventsForHour( + day: Date, + hour: number, + eventsList: CalendarEventModel[] = filteredEvents + ): CalendarEventModel[] { const hourStartMs = new Date(day.getFullYear(), day.getMonth(), day.getDate(), hour).getTime(); const hourEndMs = hourStartMs + 3_600_000; return eventsList.filter((e) => { @@ -157,9 +174,10 @@ dispatch('eventClick', event); } - $: headerText = view === 'day' - ? `${DAY_NAMES[currentDate.getDay()]}, ${MONTH_NAMES[currentDate.getMonth()]} ${currentDate.getDate()}, ${currentDate.getFullYear()}` - : `${MONTH_NAMES[currentDate.getMonth()]} ${currentDate.getFullYear()}`; + $: headerText = + view === 'day' + ? `${DAY_NAMES[currentDate.getDay()]}, ${MONTH_NAMES[currentDate.getMonth()]} ${currentDate.getDate()}, ${currentDate.getFullYear()}` + : `${MONTH_NAMES[currentDate.getMonth()]} ${currentDate.getFullYear()}`;
@@ -168,7 +186,10 @@
{#if $mobile}
- + -
@@ -232,7 +285,19 @@ class="md:hidden px-2 py-1.5 rounded-xl bg-black text-white dark:bg-white dark:text-black transition text-sm flex items-center" on:click={() => dispatch('newEvent')} > - +
@@ -244,13 +309,19 @@
{#each DAY_NAMES as day} -
{$i18n.t(day)}
+
+ {$i18n.t(day)} +
{/each}
-
+
{#each monthDays as day, i} - {@const dayKey = new Date(day.getFullYear(), day.getMonth(), day.getDate()).getTime().toString()} + {@const dayKey = new Date(day.getFullYear(), day.getMonth(), day.getDate()) + .getTime() + .toString()} {@const dayEvents = eventsByDay[dayKey] || []} {@const col = i % 7} {@const row = Math.floor(i / 7)} @@ -293,18 +364,34 @@
- + {:else if view === 'week'}
-
+
-
+
{#each weekDays as day} -
-
{DAY_NAMES[day.getDay()]}
-
+
+
+ {DAY_NAMES[day.getDay()]} +
+
{day.getDate()}
@@ -313,12 +400,22 @@
{#each hours as hour} -
-
{hour > 0 ? formatHour(hour) : ''}
+
+
+ {hour > 0 ? formatHour(hour) : ''} +
{#each weekDays as day} {@const hourEvents = getEventsForHour(day, hour, filteredEvents)}
- + {:else}
-
+
{#each hours as hour} {@const hourEvents = getEventsForHour(currentDate, hour, filteredEvents)} -
-
{hour > 0 ? formatHour(hour) : ''}
+
+
+ {hour > 0 ? formatHour(hour) : ''} +
+ + {/if}
-
{$i18n.t('Settings')}
- + {/if} + + {#if $user?.role === 'admin' || $user?.permissions?.workspace?.models || $user?.permissions?.workspace?.knowledge || $user?.permissions?.workspace?.prompts || $user?.permissions?.workspace?.tools} +
+ {/if} {#if $config?.features?.enable_automations && ($user?.role === 'admin' || $user?.permissions?.features?.automations)} - { - if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return; - e.preventDefault(); - show = false; - goto('/automations'); - if ($mobile) { - await tick(); - showSidebar.set(false); - } - }} - > - -
{$i18n.t('Automations')}
- + + + {/if} +
{/if} {#if $config?.features?.enable_calendar && ($user?.role === 'admin' || $user?.permissions?.features?.calendar)} - { - if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return; - e.preventDefault(); - show = false; - goto('/calendar'); - }} - > - -
{$i18n.t('Calendar')}
- + + + {/if} +
{/if} {#if role === 'admin'} - { - if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) { - return; - } - e.preventDefault(); - show = false; - goto('/playground'); - if ($mobile) { - await tick(); - showSidebar.set(false); - } - }} - > -
- -
-
{$i18n.t('Playground')}
-
+ {/if} +
+
+ + {#if role === 'admin'} Date: Sun, 19 Apr 2026 23:17:25 +0900 Subject: [PATCH 283/334] refac --- src/routes/(app)/calendar/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(app)/calendar/+page.svelte b/src/routes/(app)/calendar/+page.svelte index 25671fc791..6e7296efb3 100644 --- a/src/routes/(app)/calendar/+page.svelte +++ b/src/routes/(app)/calendar/+page.svelte @@ -159,7 +159,7 @@ {#if loaded}
- -
{#if ($models ?? []).length > 0 && (($settings?.pinnedModels ?? []).length > 0 || $config?.default_pinned_models)} Date: Sun, 19 Apr 2026 23:46:32 +0900 Subject: [PATCH 287/334] refac --- .../components/layout/Sidebar/UserMenu.svelte | 130 +++++++++--------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/src/lib/components/layout/Sidebar/UserMenu.svelte b/src/lib/components/layout/Sidebar/UserMenu.svelte index 05668e25d5..ec86a11a9d 100644 --- a/src/lib/components/layout/Sidebar/UserMenu.svelte +++ b/src/lib/components/layout/Sidebar/UserMenu.svelte @@ -234,50 +234,6 @@
{/if} - {#if ($config?.features?.enable_notes ?? false) && ($user?.role === 'admin' || ($user?.permissions?.features?.notes ?? true))} - - {/if} - {#if $user?.role === 'admin' || $user?.permissions?.workspace?.models || $user?.permissions?.workspace?.knowledge || $user?.permissions?.workspace?.prompts || $user?.permissions?.workspace?.tools}
{/if} - {#if $config?.features?.enable_automations && ($user?.role === 'admin' || $user?.permissions?.features?.automations)} + {#if ($config?.features?.enable_notes ?? false) && ($user?.role === 'admin' || ($user?.permissions?.features?.notes ?? true))}
{ if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return; e.preventDefault(); show = false; - goto('/automations'); + goto('/notes'); if ($mobile) { await tick(); showSidebar.set(false); @@ -353,35 +309,22 @@ }} >
- - - +
-
{$i18n.t('Automations')}
+
{$i18n.t('Notes')}
{#if shiftKey}
{/if} + {#if $config?.features?.enable_automations && ($user?.role === 'admin' || $user?.permissions?.features?.automations)} + + {/if} + {#if role === 'admin'} - {#if pinnedItems.includes('notes') && ($config?.features?.enable_notes ?? false) && ($user?.role === 'admin' || ($user?.permissions?.features?.notes ?? true))} - - {/if} - - {#if pinnedItems.includes('workspace') && ($user?.role === 'admin' || $user?.permissions?.workspace?.models || $user?.permissions?.workspace?.knowledge || $user?.permissions?.workspace?.prompts || $user?.permissions?.workspace?.tools)} - - {/if} - - {#if pinnedItems.includes('automations') && $config?.features?.enable_automations && ($user?.role === 'admin' || $user?.permissions?.features?.automations)} - - {/if} - - {#if pinnedItems.includes('calendar') && $config?.features?.enable_calendar && ($user?.role === 'admin' || $user?.permissions?.features?.calendar)} - - {/if} - - {#if pinnedItems.includes('playground') && $user?.role === 'admin'} - - {/if} + {#each pinnedItems as itemId (itemId)} + {@const meta = getMenuItemMeta(itemId)} + {#if meta && isMenuItemVisible(itemId)} + + {/if} + {/each}
From eb16ae92a5b8f93fe3fde9fba709bcfd85792f6d Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 19 Apr 2026 23:49:45 +0900 Subject: [PATCH 289/334] chore: format --- src/lib/components/layout/Sidebar.svelte | 47 ++++++++++++++++++++---- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index 10243df067..fcea714f02 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -856,7 +856,7 @@
- {#each pinnedItems as itemId (itemId)} + {#each pinnedItems as itemId (itemId)} {@const meta = getMenuItemMeta(itemId)} {#if meta && isMenuItemVisible(itemId)}
@@ -877,16 +877,49 @@ {#if itemId === 'notes'} {:else if itemId === 'workspace'} - - + + {:else if itemId === 'automations'} - - + + {:else if itemId === 'calendar'} - - + + {:else if itemId === 'playground'} From f6d1969067269ce3ff12a21ff090f73ddf88b793 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 19 Apr 2026 23:55:17 +0900 Subject: [PATCH 290/334] refac --- .../components/layout/Sidebar/UserMenu.svelte | 136 +++++++++--------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/src/lib/components/layout/Sidebar/UserMenu.svelte b/src/lib/components/layout/Sidebar/UserMenu.svelte index ec86a11a9d..d7b79752c6 100644 --- a/src/lib/components/layout/Sidebar/UserMenu.svelte +++ b/src/lib/components/layout/Sidebar/UserMenu.svelte @@ -234,6 +234,74 @@
{/if} + + + {#if role === 'admin'} + { + if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) { + return; + } + e.preventDefault(); + show = false; + goto('/admin'); + if ($mobile) { + await tick(); + showSidebar.set(false); + } + }} + > +
+ +
+
{$i18n.t('Admin Panel')}
+
+ {/if} + + + +
+ {#if $user?.role === 'admin' || $user?.permissions?.workspace?.models || $user?.permissions?.workspace?.knowledge || $user?.permissions?.workspace?.prompts || $user?.permissions?.workspace?.tools}
{/if} -
- - - - - - {#if role === 'admin'} -
{ - if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) { - return; - } - e.preventDefault(); - show = false; - goto('/admin'); - if ($mobile) { - await tick(); - showSidebar.set(false); - } - }} - > -
- -
-
{$i18n.t('Admin Panel')}
-
- {/if} - {#if help}
From 1d501cfa3f96b3a9a5f4f7ce996947671fd09f29 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 20 Apr 2026 00:07:34 +0900 Subject: [PATCH 291/334] refac --- backend/open_webui/models/calendar.py | 60 ++++++--------------------- 1 file changed, 13 insertions(+), 47 deletions(-) diff --git a/backend/open_webui/models/calendar.py b/backend/open_webui/models/calendar.py index 9afa7c15e2..4841e7b2dd 100644 --- a/backend/open_webui/models/calendar.py +++ b/backend/open_webui/models/calendar.py @@ -248,7 +248,7 @@ class CalendarTable: return CalendarModel.model_validate(cal_data) async def get_or_create_defaults(self, user_id: str, db: Optional[AsyncSession] = None) -> list[CalendarModel]: - """Return user's calendars, creating 'Personal' and 'Scheduled Tasks' if none exist.""" + """Return user's calendars, creating 'Personal' default if none exist.""" async with get_async_db_context(db) as db: result = await db.execute( select(Calendar).filter(Calendar.user_id == user_id).order_by(Calendar.created_at.asc()) @@ -259,29 +259,18 @@ class CalendarTable: return [CalendarModel.model_validate(c) for c in calendars] now = int(time.time_ns()) - defaults = [ - Calendar( - id=str(uuid4()), - user_id=user_id, - name='Personal', - color='#3b82f6', - is_default=True, - created_at=now, - updated_at=now, - ), - Calendar( - id=str(uuid4()), - user_id=user_id, - name='Scheduled Tasks', - color='#8b5cf6', - created_at=now + 1, - updated_at=now + 1, - ), - ] - for cal in defaults: - db.add(cal) + cal = Calendar( + id=str(uuid4()), + user_id=user_id, + name='Personal', + color='#3b82f6', + is_default=True, + created_at=now, + updated_at=now, + ) + db.add(cal) await db.commit() - return [CalendarModel.model_validate(c) for c in defaults] + return [CalendarModel.model_validate(cal)] async def get_calendars_by_user(self, user_id: str, db: Optional[AsyncSession] = None) -> list[CalendarModel]: """Owned + shared calendars.""" @@ -317,30 +306,7 @@ class CalendarTable: cal = result.scalars().first() return await self._to_calendar_model(cal, db=db) if cal else None - async def get_scheduled_tasks_calendar( - self, user_id: str, db: Optional[AsyncSession] = None - ) -> Optional[CalendarModel]: - """Get the user's Scheduled Tasks calendar (for automation integration).""" - async with get_async_db_context(db) as db: - result = await db.execute( - select(Calendar).filter( - Calendar.user_id == user_id, - Calendar.name == 'Scheduled Tasks', - ) - ) - cal = result.scalars().first() - if not cal: - # Ensure defaults exist then retry - await self.get_or_create_defaults(user_id, db=db) - result = await db.execute( - select(Calendar).filter( - Calendar.user_id == user_id, - Calendar.name == 'Scheduled Tasks', - ) - ) - cal = result.scalars().first() - # Lightweight return — skip access_grants loading since we only need id/color - return CalendarModel.model_validate(cal) if cal else None + async def insert_new_calendar( self, user_id: str, form_data: CalendarForm, db: Optional[AsyncSession] = None From 24dd5b461eb44d306c823389e0f664c45db042e8 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 20 Apr 2026 00:09:24 +0900 Subject: [PATCH 292/334] refac --- backend/open_webui/routers/calendar.py | 51 +++++++++++++++---- .../calendar/CalendarEventModal.svelte | 2 +- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/backend/open_webui/routers/calendar.py b/backend/open_webui/routers/calendar.py index 47093ca788..cde2ea0484 100644 --- a/backend/open_webui/routers/calendar.py +++ b/backend/open_webui/routers/calendar.py @@ -30,6 +30,8 @@ log = logging.getLogger(__name__) router = APIRouter() +SCHEDULED_TASKS_CALENDAR_ID = '__scheduled_tasks__' + async def check_calendar_permission(request: Request, user): """Check global feature flag AND per-user permission for calendar access.""" @@ -47,6 +49,17 @@ async def check_calendar_permission(request: Request, user): ) +async def _user_has_automations(request: Request, user) -> bool: + """Check if automations feature is available to this user.""" + if not getattr(request.app.state.config, 'ENABLE_AUTOMATIONS', False): + return False + if user.role == 'admin': + return True + return await has_permission( + user.id, 'features.automations', request.app.state.config.USER_PERMISSIONS + ) + + async def _check_calendar_access(calendar_id: str, user: UserModel, permission: str = 'write') -> CalendarModel: """Verify user has access to a calendar. Returns the calendar or raises 403/404.""" cal = await Calendars.get_calendar_by_id(calendar_id) @@ -74,9 +87,26 @@ async def _check_calendar_access(calendar_id: str, user: UserModel, permission: @router.get('/', response_model=list[CalendarModel]) async def get_calendars(request: Request, user: UserModel = Depends(get_verified_user)): - """List user's calendars (owned + shared). Auto-creates defaults on first call.""" + """List user's calendars (owned + shared), plus a virtual Scheduled Tasks calendar + when automations are available.""" await check_calendar_permission(request, user) - return await Calendars.get_calendars_by_user(user.id) + calendars = await Calendars.get_calendars_by_user(user.id) + + if await _user_has_automations(request, user): + now = int(time.time_ns()) + calendars.append( + CalendarModel( + id=SCHEDULED_TASKS_CALENDAR_ID, + user_id=user.id, + name='Scheduled Tasks', + color='#8b5cf6', + is_default=False, + created_at=now, + updated_at=now, + ) + ) + + return calendars @router.post('/create', response_model=CalendarModel) @@ -144,11 +174,12 @@ async def get_events( expanded.append(event) # 2. Virtual automation events (Scheduled Tasks calendar) - try: - from open_webui.models.automations import Automations, AutomationRuns + if await _user_has_automations(request, user) and ( + cal_id_list is None or SCHEDULED_TASKS_CALENDAR_ID in cal_id_list + ): + try: + from open_webui.models.automations import Automations, AutomationRuns - scheduled_cal = await Calendars.get_scheduled_tasks_calendar(user.id) - if scheduled_cal and (cal_id_list is None or scheduled_cal.id in cal_id_list): # Future runs: expand RRULEs for active automations only active_automations = await Automations.get_active_by_user(user.id) for auto in active_automations: @@ -158,7 +189,7 @@ async def get_events( virtual = { 'id': f'auto_{auto.id}', - 'calendar_id': scheduled_cal.id, + 'calendar_id': SCHEDULED_TASKS_CALENDAR_ID, 'user_id': user.id, 'title': auto.name, 'description': auto.data.get('prompt', '') if auto.data else '', @@ -190,7 +221,7 @@ async def get_events( expanded.append( CalendarEventUserResponse( id=f'run_{run.id}', - calendar_id=scheduled_cal.id, + calendar_id=SCHEDULED_TASKS_CALENDAR_ID, user_id=user.id, title=auto.name, description=run.error if run.status == 'error' else '', @@ -213,8 +244,8 @@ async def get_events( user=None, ) ) - except Exception as e: - log.warning(f'Failed to compute automation events: {e}', exc_info=True) + except Exception as e: + log.warning(f'Failed to compute automation events: {e}', exc_info=True) return [e.model_dump() if hasattr(e, 'model_dump') else e for e in expanded] diff --git a/src/lib/components/calendar/CalendarEventModal.svelte b/src/lib/components/calendar/CalendarEventModal.svelte index ad7bf9b0df..bba3b3426b 100644 --- a/src/lib/components/calendar/CalendarEventModal.svelte +++ b/src/lib/components/calendar/CalendarEventModal.svelte @@ -184,7 +184,7 @@ class="w-full text-sm bg-transparent outline-hidden cursor-pointer" bind:value={calendarId} > - {#each calendars.filter((c) => c.name !== 'Scheduled Tasks') as cal (cal.id)} + {#each calendars.filter((c) => c.id !== '__scheduled_tasks__') as cal (cal.id)} {/each} From 4e31fa4427037c0ffd4ad704308203639bf05df8 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 20 Apr 2026 00:12:53 +0900 Subject: [PATCH 293/334] refac --- .../calendar/CalendarSidebar.svelte | 2 +- .../components/calendar/CalendarView.svelte | 164 ---------------- src/routes/(app)/calendar/+page.svelte | 180 ++++++++++++++++-- 3 files changed, 170 insertions(+), 176 deletions(-) diff --git a/src/lib/components/calendar/CalendarSidebar.svelte b/src/lib/components/calendar/CalendarSidebar.svelte index 8ed0df4727..dc98df05e0 100644 --- a/src/lib/components/calendar/CalendarSidebar.svelte +++ b/src/lib/components/calendar/CalendarSidebar.svelte @@ -71,7 +71,7 @@
-
+
{miniMonthNames[miniMonth]} {miniYear}
- -
- {/if} - -
-
- {headerText} - - -
- -
- - - - - -
-
-
- - {#if view === 'month'}
diff --git a/src/routes/(app)/calendar/+page.svelte b/src/routes/(app)/calendar/+page.svelte index 6e7296efb3..2267c1c799 100644 --- a/src/routes/(app)/calendar/+page.svelte +++ b/src/routes/(app)/calendar/+page.svelte @@ -14,6 +14,11 @@ import CalendarEventModal from '$lib/components/calendar/CalendarEventModal.svelte'; import Spinner from '$lib/components/common/Spinner.svelte'; import Plus from '$lib/components/icons/Plus.svelte'; + import Tooltip from '$lib/components/common/Tooltip.svelte'; + import SidebarIcon from '$lib/components/icons/Sidebar.svelte'; + import Select from '$lib/components/common/Select.svelte'; + import Check from '$lib/components/icons/Check.svelte'; + import ChevronDown from '$lib/components/icons/ChevronDown.svelte'; const i18n = getContext('i18n'); @@ -29,6 +34,22 @@ let editEvent: CalendarEventModel | null = null; let defaultStartAt: number | null = null; + const MONTH_NAMES = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ]; + const DAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + function getVisibleRange(): { start: string; end: string } { const d = new Date(currentDate); let start: Date; @@ -128,8 +149,29 @@ showEventModal = true; } + function navigateCalendar(delta: number) { + const d = new Date(currentDate); + if (view === 'month') { + d.setDate(1); + d.setMonth(d.getMonth() + delta); + } else if (view === 'week') d.setDate(d.getDate() + delta * 7); + else d.setDate(d.getDate() + delta); + currentDate = d; + handleNavigate(); + } + + function goToToday() { + currentDate = new Date(); + handleNavigate(); + } + $: defaultCalendarId = calendars.find((c) => c.is_default)?.id || calendars[0]?.id || ''; + $: headerText = + view === 'day' + ? `${DAY_NAMES[currentDate.getDay()]}, ${MONTH_NAMES[currentDate.getMonth()]} ${currentDate.getDate()}, ${currentDate.getFullYear()}` + : `${MONTH_NAMES[currentDate.getMonth()]} ${currentDate.getFullYear()}`; + onMount(async () => { await loadCalendars(); await refresh(); @@ -157,17 +199,134 @@ : ''} max-w-full" > {#if loaded} + + +
- From e88e565ab46ed85a7bc95d45ca1057b2951810ed Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 13:18:54 +0900 Subject: [PATCH 312/334] refac --- backend/open_webui/utils/misc.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/backend/open_webui/utils/misc.py b/backend/open_webui/utils/misc.py index 441f26a918..670a94b512 100644 --- a/backend/open_webui/utils/misc.py +++ b/backend/open_webui/utils/misc.py @@ -148,22 +148,19 @@ def convert_output_to_messages(output: list, raw: bool = False) -> list[dict]: messages = [] pending_tool_calls = [] pending_content = [] - pending_reasoning = '' def flush_pending(): - nonlocal pending_content, pending_tool_calls, pending_reasoning - if pending_content or pending_tool_calls or pending_reasoning: + nonlocal pending_content, pending_tool_calls + if pending_content or pending_tool_calls: messages.append( { 'role': 'assistant', 'content': '\n'.join(pending_content) if pending_content else '', **({'tool_calls': pending_tool_calls} if pending_tool_calls else {}), - **({'reasoning_content': pending_reasoning} if pending_reasoning else {}), } ) pending_content = [] pending_tool_calls = [] - pending_reasoning = '' for item in output: item_type = item.get('type', '') @@ -248,10 +245,12 @@ def convert_output_to_messages(output: list, raw: bool = False) -> list[dict]: start_tag = item.get('start_tag', '') end_tag = item.get('end_tag', '') pending_content.append(f'{start_tag}{reasoning_text}{end_tag}') - # Preserve raw reasoning text as reasoning_content for - # providers that require it on assistant tool-call messages - # (e.g. Moonshot/Kimi K2.5). - pending_reasoning += reasoning_text + # NOTE: Some providers (e.g. Moonshot/Kimi K2.5) require + # reasoning_content as a top-level field on assistant + # messages. This should be handled externally via a + # pipeline filter or connection-level middleware, not + # here — adding it universally breaks strict providers + # (OpenAI, Vertex AI, Azure) that reject unknown fields. # else: skip reasoning blocks for normal LLM messages elif item_type == 'open_webui:code_interpreter': From 4790faba73b1fbc00a296529d4b1ced524247cc7 Mon Sep 17 00:00:00 2001 From: G30 <50341825+silentoplayz@users.noreply.github.com> Date: Tue, 21 Apr 2026 00:21:48 -0400 Subject: [PATCH 313/334] fix(ui): add shift+click to bypass message deletion confirmation (#23888) --- src/lib/components/chat/Messages/ResponseMessage.svelte | 8 ++++++-- src/lib/components/chat/Messages/UserMessage.svelte | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 487c3a59ab..2d339c6f36 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -1381,8 +1381,12 @@ class="{isLastMessage || ($settings?.highContrastMode ?? false) ? 'visible' : 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition" - on:click={() => { - showDeleteConfirm = true; + on:click={(e) => { + if (e.shiftKey) { + deleteMessageHandler(); + } else { + showDeleteConfirm = true; + } }} > { - showDeleteConfirm = true; + on:click={(e) => { + if (e.shiftKey) { + deleteMessageHandler(); + } else { + showDeleteConfirm = true; + } }} > Date: Tue, 21 Apr 2026 07:29:33 +0300 Subject: [PATCH 314/334] fix: always rAF-throttle markdown parsing during streaming (#23868) --- src/lib/components/chat/Messages/Markdown.svelte | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/lib/components/chat/Messages/Markdown.svelte b/src/lib/components/chat/Messages/Markdown.svelte index 50c50d5725..d0b54b6528 100644 --- a/src/lib/components/chat/Messages/Markdown.svelte +++ b/src/lib/components/chat/Messages/Markdown.svelte @@ -71,17 +71,11 @@ }; const updateHandler = (content) => { - if (content) { - if (done) { - cancelAnimationFrame(pendingUpdate); + if (content && !pendingUpdate) { + pendingUpdate = requestAnimationFrame(() => { pendingUpdate = null; parseTokens(); - } else if (!pendingUpdate) { - pendingUpdate = requestAnimationFrame(() => { - pendingUpdate = null; - parseTokens(); - }); - } + }); } }; From a2875f13c688c60b2f25f2d40e5026a14aa632d0 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 13:33:33 +0900 Subject: [PATCH 315/334] refac --- backend/open_webui/utils/middleware.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 036dc9bf39..0d1680eb93 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -2459,6 +2459,7 @@ async def process_chat_payload(request, form_data, user, metadata, model): tool_ids = form_data.pop('tool_ids', None) terminal_id = form_data.pop('terminal_id', None) files = form_data.pop('files', None) + form_data.pop('folder_id', None) # Caller-provided OpenAI-style tools take precedence over server-side # tool resolution (tool_ids, MCP servers, builtin tools). From 46d73c9dcd4ff7afd6c0efc98fd42f5f18cef555 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 13:46:39 +0900 Subject: [PATCH 316/334] refac --- backend/open_webui/routers/automations.py | 4 ++-- backend/open_webui/tools/builtin.py | 4 ++-- backend/open_webui/utils/automations.py | 21 ++++++++++++++++----- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/backend/open_webui/routers/automations.py b/backend/open_webui/routers/automations.py index ed33c4e8cb..4ff66feb97 100644 --- a/backend/open_webui/routers/automations.py +++ b/backend/open_webui/routers/automations.py @@ -163,7 +163,7 @@ async def create_new_automation( ): await check_automations_permission(request, user) try: - validate_rrule(form_data.data.rrule) + validate_rrule(form_data.data.rrule, tz=user.timezone) except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, @@ -213,7 +213,7 @@ async def update_automation_by_id( check_automation_access(automation, user) try: - validate_rrule(form_data.data.rrule) + validate_rrule(form_data.data.rrule, tz=user.timezone) except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, diff --git a/backend/open_webui/tools/builtin.py b/backend/open_webui/tools/builtin.py index 9c1a91abc3..afa3cb63a9 100644 --- a/backend/open_webui/tools/builtin.py +++ b/backend/open_webui/tools/builtin.py @@ -2575,7 +2575,7 @@ async def create_automation( # Validate the RRULE try: - validate_rrule(rrule) + validate_rrule(rrule, tz=user.timezone) except ValueError as e: return json.dumps({'error': f'Invalid schedule: {e}'}) @@ -2656,7 +2656,7 @@ async def update_automation( # Validate RRULE if changed if rrule is not None: try: - validate_rrule(new_rrule) + validate_rrule(new_rrule, tz=user.timezone if user else None) except ValueError as e: return json.dumps({'error': f'Invalid schedule: {e}'}) diff --git a/backend/open_webui/utils/automations.py b/backend/open_webui/utils/automations.py index 0c6e4e969a..984c8a0e4e 100644 --- a/backend/open_webui/utils/automations.py +++ b/backend/open_webui/utils/automations.py @@ -61,13 +61,19 @@ def _parse_rule(s: str): return rrulestr(s, ignoretz=True) -def validate_rrule(s: str) -> None: - """Raise ValueError if the RRULE is malformed or exhausted.""" +def validate_rrule(s: str, tz: str = None) -> None: + """Raise ValueError if the RRULE is malformed or exhausted. + + When *tz* is provided the "now" reference uses the user's local + clock so that near-future schedules are not incorrectly rejected + on servers whose system clock is ahead (e.g. UTC vs US timezones). + """ try: rule = _parse_rule(s) except Exception as e: raise ValueError(ERROR_MESSAGES.AUTOMATION_INVALID_RRULE(e)) - if rule.after(datetime.now()) is None: + now = datetime.now(ZoneInfo(tz)).replace(tzinfo=None) if tz else datetime.now() + if rule.after(now) is None: raise ValueError(ERROR_MESSAGES.AUTOMATION_NO_FUTURE_RUNS) @@ -83,10 +89,15 @@ def next_run_ns(s: str, tz: str = None) -> Optional[int]: def next_n_runs_ns(s: str, n: int = 5, tz: str = None) -> list[int]: - """Compute next N occurrences for UI preview.""" + """Compute next N occurrences for UI preview. + + Uses the user's timezone for the starting "now" so that the + preview matches the user's local clock (same as next_run_ns). + """ rule = _parse_rule(s) result = [] - dt = datetime.now() + now = datetime.now(ZoneInfo(tz)).replace(tzinfo=None) if tz else datetime.now() + dt = now for _ in range(n): dt = rule.after(dt) if not dt: From 65834432a38c483421d41da50ebe981166e59053 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 13:51:39 +0900 Subject: [PATCH 317/334] refac --- backend/open_webui/utils/automations.py | 33 +++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/backend/open_webui/utils/automations.py b/backend/open_webui/utils/automations.py index 984c8a0e4e..95b931e320 100644 --- a/backend/open_webui/utils/automations.py +++ b/backend/open_webui/utils/automations.py @@ -45,6 +45,22 @@ CALENDAR_ALERT_LOOKAHEAD_MINUTES = int(os.getenv('CALENDAR_ALERT_LOOKAHEAD_MINUT #################### +def _resolve_tz(tz: str = None) -> Optional[ZoneInfo]: + """Safely resolve a timezone string to ZoneInfo. + + Returns None (→ server-local fallback) when *tz* is empty, None, + or an unrecognised IANA key. Logs a warning on bad keys so + misconfiguration is visible in the server logs. + """ + if not tz: + return None + try: + return ZoneInfo(tz) + except (KeyError, Exception): + log.warning('Unknown timezone %r — falling back to server time', tz) + return None + + def _parse_rule(s: str): """Parse RRULE with clock-aligned DTSTART for sub-daily frequencies. @@ -72,19 +88,21 @@ def validate_rrule(s: str, tz: str = None) -> None: rule = _parse_rule(s) except Exception as e: raise ValueError(ERROR_MESSAGES.AUTOMATION_INVALID_RRULE(e)) - now = datetime.now(ZoneInfo(tz)).replace(tzinfo=None) if tz else datetime.now() + zi = _resolve_tz(tz) + now = datetime.now(zi).replace(tzinfo=None) if zi else datetime.now() if rule.after(now) is None: raise ValueError(ERROR_MESSAGES.AUTOMATION_NO_FUTURE_RUNS) def next_run_ns(s: str, tz: str = None) -> Optional[int]: """Next occurrence as epoch nanoseconds, respecting user timezone.""" - now = datetime.now(ZoneInfo(tz)) if tz else datetime.now() + zi = _resolve_tz(tz) + now = datetime.now(zi) if zi else datetime.now() dt = _parse_rule(s).after(now.replace(tzinfo=None)) if dt is None: return None - if tz: - dt = dt.replace(tzinfo=ZoneInfo(tz)) + if zi: + dt = dt.replace(tzinfo=zi) return int(dt.timestamp() * 1_000_000_000) @@ -94,16 +112,17 @@ def next_n_runs_ns(s: str, n: int = 5, tz: str = None) -> list[int]: Uses the user's timezone for the starting "now" so that the preview matches the user's local clock (same as next_run_ns). """ + zi = _resolve_tz(tz) rule = _parse_rule(s) result = [] - now = datetime.now(ZoneInfo(tz)).replace(tzinfo=None) if tz else datetime.now() + now = datetime.now(zi).replace(tzinfo=None) if zi else datetime.now() dt = now for _ in range(n): dt = rule.after(dt) if not dt: break - if tz: - dt_tz = dt.replace(tzinfo=ZoneInfo(tz)) + if zi: + dt_tz = dt.replace(tzinfo=zi) result.append(int(dt_tz.timestamp() * 1_000_000_000)) else: result.append(int(dt.timestamp() * 1_000_000_000)) From f485309fd69816dcd025af00717db2b9d422a7dc Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 13:57:43 +0900 Subject: [PATCH 318/334] refac --- src/app.css | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/app.css b/src/app.css index ac02afcb1d..9352177bd8 100644 --- a/src/app.css +++ b/src/app.css @@ -260,10 +260,15 @@ select { display: none; } -/* Hide leaked Mermaid temp containers if render cleanup misses */ +/* Hide leaked Mermaid temp containers if render cleanup misses. + Use visibility:hidden (not display:none) so mermaid can still + measure the SVG layout before extracting its HTML. */ body > div[id^='dmermaid-'], body > iframe[id^='imermaid-'] { - display: none !important; + position: fixed !important; + visibility: hidden !important; + height: 0 !important; + overflow: hidden !important; } .scrollbar-hidden:active::-webkit-scrollbar-thumb, From a27916d1dbd9bc6890f35acb7228e1f2463a3409 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 14:31:04 +0900 Subject: [PATCH 319/334] refac --- backend/open_webui/functions.py | 17 +++++++++++++++-- backend/open_webui/utils/middleware.py | 23 +++++++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/backend/open_webui/functions.py b/backend/open_webui/functions.py index 8bfc2c2b08..1e032759ea 100644 --- a/backend/open_webui/functions.py +++ b/backend/open_webui/functions.py @@ -234,11 +234,24 @@ async def generate_function_chat_completion(request, form_data, user, models: di oauth_token = None try: - if request.cookies.get('oauth_session_id', None): + oauth_session_id = request.cookies.get('oauth_session_id', None) + if oauth_session_id: oauth_token = await request.app.state.oauth_manager.get_oauth_token( user.id, - request.cookies.get('oauth_session_id', None), + oauth_session_id, ) + + # Fallback: no cookie (automation, API key, etc.) — use most recent session + if oauth_token is None: + from open_webui.models.oauth_sessions import OAuthSessions + + sessions = await OAuthSessions.get_sessions_by_user_id(user.id) + if sessions: + best = max(sessions, key=lambda s: s.updated_at) + oauth_token = await request.app.state.oauth_manager.get_oauth_token( + user.id, + best.id, + ) except Exception as e: log.error(f'Error getting OAuth token: {e}') diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 0d1680eb93..8d3b6dd267 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -2916,13 +2916,32 @@ def build_response_object(response, response_data): async def get_system_oauth_token(request, user): + """Get the system OAuth token for a user. + + Primary path: use the oauth_session_id cookie (browser requests). + Fallback: look up the user's most recent OAuth session from the DB + (covers automations, API calls, and other cookie-less contexts). + """ oauth_token = None try: - if request.cookies.get('oauth_session_id', None): + oauth_session_id = request.cookies.get('oauth_session_id', None) + if oauth_session_id: oauth_token = await request.app.state.oauth_manager.get_oauth_token( user.id, - request.cookies.get('oauth_session_id', None), + oauth_session_id, ) + + # Fallback: no cookie (automation, API key, etc.) — use most recent session + if oauth_token is None: + from open_webui.models.oauth_sessions import OAuthSessions + + sessions = await OAuthSessions.get_sessions_by_user_id(user.id) + if sessions: + best = max(sessions, key=lambda s: s.updated_at) + oauth_token = await request.app.state.oauth_manager.get_oauth_token( + user.id, + best.id, + ) except Exception as e: log.error(f'Error getting OAuth token: {e}') return oauth_token From c4aac0415cf89b535edf1700473c50dc22f4fb64 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 14:58:28 +0900 Subject: [PATCH 320/334] refac --- backend/open_webui/internal/db.py | 120 +++++++++++++++++++++++++-- backend/open_webui/migrations/env.py | 5 ++ 2 files changed, 120 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/internal/db.py b/backend/open_webui/internal/db.py index d1c4060cae..e3b4a110cd 100644 --- a/backend/open_webui/internal/db.py +++ b/backend/open_webui/internal/db.py @@ -1,8 +1,10 @@ import os import json import logging +import ssl as _stdlib_ssl from contextlib import asynccontextmanager, contextmanager from typing import Any, Optional +from urllib.parse import parse_qs, urlencode, urlparse, urlunparse from open_webui.internal.wrappers import register_connection from open_webui.env import ( @@ -35,6 +37,96 @@ from typing_extensions import Self log = logging.getLogger(__name__) +def extract_ssl_mode_from_url(url: str) -> tuple[str, str | None]: + """Strip SSL query-string parameters from a PostgreSQL URL. + + asyncpg and psycopg2 use different query-string keys for SSL + (``ssl`` vs ``sslmode``). This helper removes **both** from the + URL so that each driver can receive the correct parameter through + its own mechanism (query-string re-injection for psycopg2, + ``connect_args`` for asyncpg). + + Returns + ------- + (url_without_ssl, ssl_mode) + *url_without_ssl* is the original URL with ``ssl`` / ``sslmode`` + query parameters removed. *ssl_mode* is the extracted mode + string (e.g. ``'require'``), or ``None`` if neither parameter + was present. + + Non-PostgreSQL URLs are returned unchanged with ``ssl_mode=None``. + """ + if not url or not any( + url.startswith(prefix) + for prefix in ('postgresql://', 'postgresql+', 'postgres://') + ): + return url, None + + parsed = urlparse(url) + query_params = parse_qs(parsed.query, keep_blank_values=True) + + # Prefer sslmode (libpq canonical) over the asyncpg-only ssl key. + ssl_mode: str | None = None + for key in ('sslmode', 'ssl'): + values = query_params.pop(key, None) + if values and ssl_mode is None: + ssl_mode = values[0] + + if ssl_mode is None: + # Nothing to strip — return the URL untouched. + return url, None + + # Rebuild the query string without the SSL keys. + remaining_query = urlencode(query_params, doseq=True) + url_without_ssl = urlunparse(parsed._replace(query=remaining_query)) + return url_without_ssl, ssl_mode + + +def build_asyncpg_ssl_args(ssl_mode: str | None) -> dict: + """Convert a libpq-style SSL mode value to asyncpg ``connect_args``. + + Returns a dict suitable for unpacking into + ``create_async_engine(..., connect_args=...)``. + """ + if ssl_mode is None: + return {} + + mode = ssl_mode.lower() + if mode == 'disable': + return {'connect_args': {'ssl': False}} + if mode in ('allow', 'prefer'): + # asyncpg has no direct equivalent — omit to let it try without. + return {} + if mode == 'require': + # SSL required but no certificate verification (matches libpq). + ctx = _stdlib_ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = _stdlib_ssl.CERT_NONE + return {'connect_args': {'ssl': ctx}} + if mode in ('verify-ca', 'verify-full'): + # Full verification — use the system trust store. + ctx = _stdlib_ssl.create_default_context() + if mode == 'verify-ca': + ctx.check_hostname = False + return {'connect_args': {'ssl': ctx}} + + # Unknown value — pass through as-is and let asyncpg decide. + return {'connect_args': {'ssl': ssl_mode}} + + +def reattach_ssl_mode_to_url(url_without_ssl: str, ssl_mode: str | None) -> str: + """Re-append ``sslmode=`` to a cleaned PostgreSQL URL. + + Used for psycopg2 / libpq consumers that expect the canonical + ``sslmode`` query-string key. + """ + if ssl_mode is None: + return url_without_ssl + separator = '&' if '?' in url_without_ssl else '?' + return f'{url_without_ssl}{separator}sslmode={ssl_mode}' + + + class JSONField(types.TypeDecorator): impl = types.Text cache_ok = True @@ -60,10 +152,14 @@ class JSONField(types.TypeDecorator): # Workaround to handle the peewee migration # This is required to ensure the peewee migration is handled before the alembic migration def handle_peewee_migration(DATABASE_URL): - # db = None + db = None try: + # Normalize SSL params so psycopg2 always sees `sslmode=` (never `ssl=`). + url_without_ssl, ssl_mode = extract_ssl_mode_from_url(DATABASE_URL) + normalized_url = reattach_ssl_mode_to_url(url_without_ssl, ssl_mode) + # Replace the postgresql:// with postgres:// to handle the peewee migration - db = register_connection(DATABASE_URL.replace('postgresql://', 'postgres://')) + db = register_connection(normalized_url.replace('postgresql://', 'postgres://')) migrate_dir = OPEN_WEBUI_DIR / 'internal' / 'migrations' router = Router(db, logger=log, migrate_dir=migrate_dir) router.run() @@ -79,14 +175,20 @@ def handle_peewee_migration(DATABASE_URL): db.close() # Assert if db connection has been closed - assert db.is_closed(), 'Database connection is still open.' + if db is not None: + assert db.is_closed(), 'Database connection is still open.' if ENABLE_DB_MIGRATIONS: handle_peewee_migration(DATABASE_URL) -SQLALCHEMY_DATABASE_URL = DATABASE_URL +# Normalize SSL params from the URL once; each engine branch re-injects +# the driver-appropriate form. +DATABASE_URL_WITHOUT_SSL, DATABASE_SSL_MODE = extract_ssl_mode_from_url(DATABASE_URL) + +# For psycopg2 (sync engine), re-append sslmode=. +SQLALCHEMY_DATABASE_URL = reattach_ssl_mode_to_url(DATABASE_URL_WITHOUT_SSL, DATABASE_SSL_MODE) if DATABASE_SSL_MODE else DATABASE_URL def _make_async_url(url: str) -> str: @@ -229,7 +331,8 @@ get_db = contextmanager(get_session) # ASYNC ENGINE (used for ALL runtime database operations) # ============================================================ -ASYNC_SQLALCHEMY_DATABASE_URL = _make_async_url(SQLALCHEMY_DATABASE_URL) +# Use the SSL-stripped URL for asyncpg — SSL is injected via connect_args. +ASYNC_SQLALCHEMY_DATABASE_URL = _make_async_url(DATABASE_URL_WITHOUT_SSL if DATABASE_SSL_MODE else SQLALCHEMY_DATABASE_URL) if 'sqlite' in ASYNC_SQLALCHEMY_DATABASE_URL: # Generous default — async coroutines + no session sharing = high connection demand. @@ -251,6 +354,10 @@ if 'sqlite' in ASYNC_SQLALCHEMY_DATABASE_URL: def _set_sqlite_pragmas(dbapi_connection, connection_record): _apply_sqlite_pragmas(dbapi_connection) else: + # Inject asyncpg-compatible SSL connect_args when the user specified + # sslmode/ssl in DATABASE_URL. + asyncpg_ssl_args = build_asyncpg_ssl_args(DATABASE_SSL_MODE) + if isinstance(DATABASE_POOL_SIZE, int): if DATABASE_POOL_SIZE > 0: async_engine = create_async_engine( @@ -260,17 +367,20 @@ else: pool_timeout=DATABASE_POOL_TIMEOUT, pool_recycle=DATABASE_POOL_RECYCLE, pool_pre_ping=True, + **asyncpg_ssl_args, ) else: async_engine = create_async_engine( ASYNC_SQLALCHEMY_DATABASE_URL, pool_pre_ping=True, poolclass=NullPool, + **asyncpg_ssl_args, ) else: async_engine = create_async_engine( ASYNC_SQLALCHEMY_DATABASE_URL, pool_pre_ping=True, + **asyncpg_ssl_args, ) diff --git a/backend/open_webui/migrations/env.py b/backend/open_webui/migrations/env.py index 3840cb4a17..f5e57920ea 100644 --- a/backend/open_webui/migrations/env.py +++ b/backend/open_webui/migrations/env.py @@ -5,6 +5,7 @@ from alembic import context from open_webui.models.auths import Auth from open_webui.models.calendar import Calendar, CalendarEvent, CalendarEventAttendee # noqa: F401 from open_webui.env import DATABASE_URL, DATABASE_PASSWORD, LOG_FORMAT +from open_webui.internal.db import extract_ssl_mode_from_url, reattach_ssl_mode_to_url from sqlalchemy import engine_from_config, pool, create_engine # this is the Alembic Config object, which provides @@ -36,6 +37,10 @@ target_metadata = Auth.metadata DB_URL = DATABASE_URL +# Normalize SSL query params for psycopg2 (Alembic uses psycopg2, not asyncpg). +url_without_ssl, ssl_mode = extract_ssl_mode_from_url(DB_URL) +DB_URL = reattach_ssl_mode_to_url(url_without_ssl, ssl_mode) if ssl_mode else DB_URL + if DB_URL: config.set_main_option('sqlalchemy.url', DB_URL.replace('%', '%%')) From 7fd94b0e73b87fcbd5f8f37898bf617c762a6ace Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 15:15:00 +0900 Subject: [PATCH 321/334] refac --- src/lib/components/chat/MessageInput.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index 92de0c3d43..aeb96af5b0 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -1941,7 +1941,8 @@ {#if !history?.currentId || history.messages[history.currentId]?.done == true} - {#if terminalCapableModels.length > 0 && (($terminalServers ?? []).length > 0 || ($settings?.terminalServers ?? []).some((s) => s.url))} + {@const hasDirectToolServerAccess = $_user?.role === 'admin' || ($_user?.permissions?.features?.direct_tool_servers ?? true)} + {#if terminalCapableModels.length > 0 && (($terminalServers ?? []).some((t) => t.id) || (hasDirectToolServerAccess && (($terminalServers ?? []).some((t) => !t.id) || ($settings?.terminalServers ?? []).some((s) => s.url))))} {/if} From 0e3135f8dc203f94f5fe30e94039a7977b2b2059 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Tue, 21 Apr 2026 08:18:33 +0200 Subject: [PATCH 322/334] chore: changelog (#23187) * chore: add changelog entry for v0.8.13 * changelog: task management, admin model deletion * changelog: emoji, shortcode, input * changelog: swipe-to-reply mobile gesture * changelog: emoji, recently-used, picker * changelog: files, chat-input, attachments * changelog: terminal session tracking, task list visibility * changelog: move terminal session tracking to Added section * changelog: performance, shared chat deletion * changelog: user activity tracking, shared chat deletion optimizations * changelog: add Russian translation entry * changelog: MCP tool server timeout configuration * changelog: image viewer memory optimization * changelog: error message persistence during streaming * changelog: codespan, animation, streaming * changelog: streaming, performance, yield * changelog: text, animation, streaming * changelog: websearch, settings, fix * changelog: automation, scheduling, workflows * changelog: automations, permissions, access * changelog: automations, editor, logs * changelog: german, completion, tokens * changelog: streaming, entities, defaults * changelog: pyodide, cache, prompt * changelog: details, expansion, settings * changelog: unread, sidebar, automations * changelog: oauth, gravatar, prompts * changelog: wake-lock, writing, retrieval * changelog: mcp, sidebar, usage * changelog: oauth, citations, sidebar * changelog: oauth, cookies, tools * changelog: translations, tamil, localization * changelog: tasks, fallback, stability * changelog: title, query, performance * changelog: sidebar, archived, menu * changelog: input, drafts, uploads * changelog: notes, permissions, security * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog date * reorder changelog entries * restore changelog ordering * restore changelog * changelog updates * adjust changelog ordering * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * Update CHANGELOG.md * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * Update CHANGELOG.md * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog * changelog --- CHANGELOG.md | 235 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 126b19e028..47f6a27199 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,241 @@ 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.9.0] - 2026-04-20 + +### Added + +- 🖥️ **Native desktop app availability.** Open WebUI is now available as a cross-platform desktop app with local model support, multi-server switching, and offline-ready usage after first launch. [#8262](https://github.com/open-webui/open-webui/issues/8262), [Desktop](https://github.com/open-webui/desktop) +- 🤖 **Scheduled chat automations.** Users can now create, schedule, run, and manage recurring automations from both the dedicated automations page and built-in chat tools, with execution logs, direct run controls, and permission-aware access control for user and group policies. [#23303](https://github.com/open-webui/open-webui/pull/23303), [Commit](https://github.com/open-webui/open-webui/commit/5a2ff8b2e5b6f55a20f7ed491f818490eb535ea7), [Commit](https://github.com/open-webui/open-webui/commit/d30a0531d4add045c21a2368d6321a9b1906865f), [Commit](https://github.com/open-webui/open-webui/commit/bae5ff938ac88a3a647cc31ca8db1101015ae18b), [Commit](https://github.com/open-webui/open-webui/commit/588b81eedaacbfd7394b707ae1600d9fb729b809..674695918e5e3e1811314ce2a082c5bbb42d76b2) +- 🧰 **Automation tools in chat.** Built-in chat tools can now create, update, list, pause, and delete scheduled automations directly in conversation when automation access is enabled. [Commit](https://github.com/open-webui/open-webui/commit/588b81eedaacbfd7394b707ae1600d9fb729b809..674695918e5e3e1811314ce2a082c5bbb42d76b2) +- 🤖 **Automation model selection reliability.** Automations created from chat now consistently use the calling model, avoiding mismatches when tool calls run under different model contexts. [Commit](https://github.com/open-webui/open-webui/commit/e709d6812f7fba246c4b7907f9fa41f751717566), [Commit](https://github.com/open-webui/open-webui/commit/398718d5059ce2a5614e9e124f20ef48b843ce42), [#23812](https://github.com/open-webui/open-webui/pull/23812) +- ⏱️ **Automation scheduling limits.** Administrators can now set "AUTOMATION_MAX_COUNT" and "AUTOMATION_MIN_INTERVAL" to limit how many automations each non-admin user can create and prevent overly frequent schedules that could overload the system. [Commit](https://github.com/open-webui/open-webui/commit/406251c2f358ffabce4d631c98c6f2c879feae5c) +- 🧭 **Global automations toggle.** Administrators can now disable automations system-wide with the "ENABLE_AUTOMATIONS" setting, which hides automation pages and tools and pauses background automation processing until it is re-enabled. [Commit](https://github.com/open-webui/open-webui/commit/42694c7c0cc8ba586c1dd364ecfaa0b4080b6cad) +- 📋 **Task management tool.** AI models can now create, update, and track tasks within a chat conversation, breaking down complex requests into manageable steps with real-time status updates. [Commit](https://github.com/open-webui/open-webui/commit/bcb71bb5206ac01d97a39fde8ecf0e0541dde636) +- 🗓️ **Calendar workspace and event management.** Users can now manage personal and shared calendars from a dedicated Calendar page, create and edit events (including recurring events), and view scheduled automations directly alongside calendar activity. [#23880](https://github.com/open-webui/open-webui/pull/23880) +- 🔐 **Calendar permission controls.** Administrators can now control calendar access through feature permissions, so calendar pages, APIs, and built-in calendar tools are available only to users and groups with calendar access enabled. [Commit](https://github.com/open-webui/open-webui/commit/5afc258c5b13f456be528420513ade546c5e86f9), [Commit](https://github.com/open-webui/open-webui/commit/37eba1c5a66b3145c122a6b40e5c29707526d121) +- 🗑️ **Calendar deletion controls.** Calendar sidebar entries now include a delete action with confirmation, allowing users to remove custom calendars directly from the Calendar page. [Commit](https://github.com/open-webui/open-webui/commit/51627555bf356c8ec663f4d2f43f2f013eadbce4) +- 🔔 **Calendar reminders and alerts.** Calendar events now support reminder options from no alert up to one hour before start time, with upcoming alerts delivered through in-app toasts, browser notifications, and optional webhooks while avoiding duplicate sends. [Commit](https://github.com/open-webui/open-webui/commit/e5b5a174265d6710e986f6534ee7e3b2923233be) +- ⚙️ **Scheduler reminder configuration.** Administrators can now configure calendar reminder processing with "SCHEDULER_POLL_INTERVAL" and "CALENDAR_ALERT_LOOKAHEAD_MINUTES", while existing "AUTOMATION_POLL_INTERVAL" setups continue to work as a legacy fallback. [Commit](https://github.com/open-webui/open-webui/commit/e5b5a174265d6710e986f6534ee7e3b2923233be) +- 🗓️ **Unified calendar header controls.** The Calendar page now uses a single top navigation bar for date navigation, view selection, and quick event creation, with improved mobile behavior and label truncation for tighter screens. [Commit](https://github.com/open-webui/open-webui/commit/4e31fa4427037c0ffd4ad704308203639bf05df8), [Commit](https://github.com/open-webui/open-webui/commit/3e3f138d9323987a41b1e3c17721a0047cf8e40f) +- 🧰 **Dedicated task checklist tools.** Built-in task tracking exposes separate tools for creating task lists and updating individual task statuses, giving multi-step chats clearer progress control. [Commit](https://github.com/open-webui/open-webui/commit/a35926261646f8897ba71da1572ed5dff802e3be) +- ☁️ **Azure responses support.** Azure OpenAI connections now support the newer "/openai/v1" format, enabling chat, responses, and proxy calls to work correctly with that endpoint style. [#23484](https://github.com/open-webui/open-webui/pull/23484) +- 🤖 **Ollama responses support.** The Ollama proxy now supports the Responses API, letting clients use "/v1/responses" directly with Ollama-hosted models through Open WebUI. [#23483](https://github.com/open-webui/open-webui/pull/23483) +- 🧩 **Responses tool output rendering.** Built-in tool outputs in Responses API flows now render more consistently so downstream chat output is easier to interpret. [Commit](https://github.com/open-webui/open-webui/commit/e695d854f2d11fada84d5fbec8d3edea4e468e19), [#23482](https://github.com/open-webui/open-webui/pull/23482) +- 🔎 **Responses citation visibility.** Responses API flows now emit citation sources more consistently, making linked references easier to preserve and display in chat output. [Commit](https://github.com/open-webui/open-webui/commit/e695d854f2d11fada84d5fbec8d3edea4e468e19), [#23774](https://github.com/open-webui/open-webui/issues/23774) +- 📎 **Attach previously uploaded files.** The chat input menu now includes a Files tab for browsing and attaching previously uploaded files, eliminating the need to re-upload files you have already shared. [Commit](https://github.com/open-webui/open-webui/commit/edb8971c7dbd974322c3207c4655ff66479c3ee2) +- 🖥️ **Terminal session tracking.** Open Terminal now tracks the current working directory per chat session, so relative paths and navigation work correctly across multiple interactions. [Commit](https://github.com/open-webui/open-webui/commit/a06685a47b89fb19dd6124fbe391ff78b54f451d), [Commit](https://github.com/open-webui/open-webui/commit/6512e085c4e56897dd49e56aff5d616820a962f3) +- 🧷 **Default model terminal selection.** Workspace model editors can now preselect an Open Terminal connection, so new chats automatically start with the model’s configured terminal ready to use. [Commit](https://github.com/open-webui/open-webui/commit/47d413ce7b2a006a8126f4a9055b13e5fcb33a1d), [#23605](https://github.com/open-webui/open-webui/issues/23605) +- 🎙️ **Mistral TTS support.** Mistral can now be used as a text-to-speech provider, with admin settings for the API key, base URL, voices, and model selection. [Commit](https://github.com/open-webui/open-webui/commit/4cee67e2be0c80a0b501073ea49a80d13efd1c41) +- 🎧 **STT preprocessing bypass option.** Administrators can now enable "AUDIO_STT_SKIP_PREPROCESSING" to send audio files directly to the speech-to-text backend, reducing memory and CPU consumption during large uploads for better transcription performance and stability on constrained deployments. [#23661](https://github.com/open-webui/open-webui/pull/23661) +- 🗑️ **Admin model deletion.** Administrators can now delete Ollama models directly from the model selector menu, making it easier to clean up unused or unwanted models. [Commit](https://github.com/open-webui/open-webui/commit/2388dd7dc3530b5dd5419c5d0bb1bcdcb7544099) +- 🔌 **Backend outlet filters for local and persisted chats.** Pipeline and function outlet filters now run reliably in backend completion flows for persisted chats and temporary local chats. [#3237](https://github.com/open-webui/open-webui/issues/3237), [Commit](https://github.com/open-webui/open-webui/commit/cf4218e688def6f11d195aeda6665ae5b5376b67) +- 🎨 **Emoji shortcode support.** Typing a colon in the chat input now opens an emoji suggestion menu, making it easier to insert emojis using shortcodes like :wave:. [Commit](https://github.com/open-webui/open-webui/commit/2040095050056d01c61aa597c5010445449a42c7) +- 📌 **Recently used emojis.** The emoji picker now shows your most recently used emojis at the top, making it faster to find emojis you use often. [Commit](https://github.com/open-webui/open-webui/commit/64da99a32218171d41b3af5acc14783de8dbdf49) +- 👆 **Swipe to reply on mobile.** Swiping right on a message now triggers a reply, making it easier to respond on touch devices with a natural gesture. [Commit](https://github.com/open-webui/open-webui/commit/012ce95f27d57bea8911bd63bfb923443c5797ae) +- 📱 **Screen-awake voice recording.** Voice recording now keeps the screen awake during active dictation and safely re-acquires wake lock after visibility changes, helping prevent long transcriptions from being cut off on mobile devices. [#23145](https://github.com/open-webui/open-webui/issues/23145) +- ✨ **Improved task list visibility.** The task list automatically hides once all tasks are complete and generation is finished, keeping the chat interface cleaner. [Commit](https://github.com/open-webui/open-webui/commit/0ad397c0482004173d4a8bf4722100acc43db454), [Commit](https://github.com/open-webui/open-webui/commit/4b35d70078a2d7a322566699a43594b3c10b2dda) +- 🔔 **Unread chat indicators.** Sidebar chats now show unread status and are marked as read when opened, making it easier to spot conversations with new activity. [Commit](https://github.com/open-webui/open-webui/commit/0638b9f56ce1ba8a496d0e84da2e7fa178b01a3f) +- 🔌 **WebSocket reconnect status feedback.** Open WebUI now warns when the real-time connection drops and confirms when it reconnects, while avoiding a reconnect message on the initial page load. [Commit](https://github.com/open-webui/open-webui/commit/1824e69a70e756cfcf543a9fbe4b0780d9b57292) +- 📍 **Pinned notes in sidebar.** Notes can now be pinned to the sidebar for quick access, and you can also create a new note directly from the pinned notes section. [Commit](https://github.com/open-webui/open-webui/commit/ecd74f220c7dd671d5705189a3f4493a3868c8bf), [Commit](https://github.com/open-webui/open-webui/commit/f1be85d997439b49fc143d2bcd2dc710f44446c8) +- 🗂️ **Model selector focus.** The model selector now resets its search only when it opens, making the popup feel more predictable while still focusing the search field automatically. [Commit](https://github.com/open-webui/open-webui/commit/b89019a8e1f96e01dc8e19a81ef8fb4f4eae3eef) +- 🗂️ **Model selector layout.** The model selector now behaves more predictably as a custom popup, and the completions playground uses a simpler model picker for easier selection. [Commit](https://github.com/open-webui/open-webui/commit/c40ea7f29d34fa9535cdf9ffe599f4429ff3f455) +- 🎚️ **Active filter valve shortcut.** Active filter badges now expose valve configuration directly in the chat input area, so filter tuning is faster during conversations. [Commit](https://github.com/open-webui/open-webui/commit/3c22afc5a67404047797921185aca984b10b45cd), [#23811](https://github.com/open-webui/open-webui/issues/23811), [#23813](https://github.com/open-webui/open-webui/pull/23813) +- 🎨 **Theme updates.** Other windows can now update the app theme directly, keeping the interface in sync when theme changes are triggered externally. [Commit](https://github.com/open-webui/open-webui/commit/9f1b279e88bd22dfff4d2531209536dea6a2f65e) +- 🚀 **Async performance and responsiveness improvements.** The core backend database and request paths now run asynchronously across the application, massively improving responsiveness and performance under concurrent load and reducing request blocking during heavy activity. [Commit](https://github.com/open-webui/open-webui/commit/27169124f220e5cea21c88601c731c3749496ab0), [Commit](https://github.com/open-webui/open-webui/commit/8936721414a17832852a90f3ee592af5a8b7232d) +- ⚡ **Drawer performance and memory optimization.** Drawer interactions now stay smoother over long sessions by removing stale keyboard listeners on teardown, which reduces memory growth and avoids accumulated event handling overhead. [#23724](https://github.com/open-webui/open-webui/pull/23724#issuecomment-4245840810) +- 🚀 **Chat history memory culling.** Long conversations now stay much more responsive by rendering a smaller message window and unloading off-screen messages with spacer-based virtualization, significantly reducing memory pressure and UI freezing on heavy chats and mobile devices. [#23067](https://github.com/open-webui/open-webui/issues/23067), [Commit](https://github.com/open-webui/open-webui/commit/026903399be73ac4b6c226647110e5662d043a50), [Commit](https://github.com/open-webui/open-webui/commit/9dccd29c94875e6f0ac373c5802cb183296e47ff) +- 🧵 **Async file and knowledge processing performance.** File processing, knowledge reindexing, and channel message helper paths now consistently await async operations, preventing skipped processing steps and improving reliability and performance of indexing and tool responses. [Commit](https://github.com/open-webui/open-webui/commit/de27a121511a31606f250ba4033490797216a0eb) +- 🚀 **Persistent chat payload efficiency.** Persisted chats now use server-side history loading instead of repeatedly resending full message payloads, improving multimodal performance and reducing stale-history overwrite risk across devices. [#19064](https://github.com/open-webui/open-webui/issues/19064), [Commit](https://github.com/open-webui/open-webui/commit/18fe17127a7175579506e7456d3e5aba201371e6), [Commit](https://github.com/open-webui/open-webui/commit/cf4218e688def6f11d195aeda6665ae5b5376b67) +- 🧵 **Non-blocking file storage operations.** Uploading, reading, transcribing, and deleting files now offloads storage I/O to background threads, keeping the application responsive during file-heavy workflows. [Commit](https://github.com/open-webui/open-webui/commit/4866bec0f238198a721c952fe18dd04ba643be33) +- 🏃 **Faster automation list loading.** The automations page now loads more smoothly by batching latest-run lookups and avoiding duplicate initial fetches. [Commit](https://github.com/open-webui/open-webui/commit/09f6d7ba57d2aaad83ad0d29d005feb7157776a1) +- 🏎️ **Streaming response performance.** Streaming responses now process each output line in a single step instead of two separate yields, reducing async overhead and improving responsiveness during long-running generations. [#23266](https://github.com/open-webui/open-webui/pull/23266) +- 🔎 **Faster mention parsing.** Chat text with HTML-like content, file paths, or tool output now parses mentions more efficiently, which helps keep typing and rendering responsive in messages that contain many '<' characters. [#23551](https://github.com/open-webui/open-webui/pull/23551) +- 🧪 **Code block rendering performance.** Code blocks now reuse a shared HTML unescape helper, reducing extra browser work when displaying encoded output in chat. [#23553](https://github.com/open-webui/open-webui/pull/23553) +- 🚀 **Inline code rendering performance.** Inline code tokens in streaming responses now fade in with a lightweight CSS animation, making chat output feel smoother while reducing interface overhead during rapid token updates. [#23258](https://github.com/open-webui/open-webui/pull/23258) +- 🎞️ **Streaming text token animation performance.** Streaming text tokens now use a lightweight CSS intro animation, making output feel smoother while reducing transition overhead and preventing tokens from fading out when generation completes. [#23257](https://github.com/open-webui/open-webui/pull/23257) +- 🎯 **Template token scan optimization.** Streaming responses now skip unnecessary token-replacement processing when no template markers are present, reducing per-update overhead and keeping chat output smoother during rapid generation. [#23161](https://github.com/open-webui/open-webui/pull/23161) +- 🔬 **Chinese text processing guard performance.** Streaming responses without Chinese characters now skip unnecessary Chinese-format processing checks, reducing per-update overhead and keeping output smoother during rapid generation. [#23162](https://github.com/open-webui/open-webui/pull/23162) +- 🧠 **HTML entity decode performance.** Streaming text decoding now avoids repeated document parsing for HTML entity handling, reducing memory churn and improving responsiveness in token-heavy chat output. [#23165](https://github.com/open-webui/open-webui/pull/23165) +- 🏷️ **Chat title update performance.** Chat title updates now run in a single database operation instead of multiple round trips, improving responsiveness and reducing overhead when titles are generated or renamed. [#23214](https://github.com/open-webui/open-webui/pull/23214) +- 📂 **Faster chat list queries performance.** Chat and folder lists now load more efficiently by fetching only the fields needed for sidebar views, improving responsiveness when browsing large conversation histories. [Commit](https://github.com/open-webui/open-webui/commit/0e5696de74cc0ba55b24cfc3d02efa83f08d7d3f) +- 📈 **Sidebar memory optimization.** Sidebar chat items now use shared drag-preview resources and safer listener cleanup, reducing memory growth and keeping large chat lists more responsive during long sessions. [#23209](https://github.com/open-webui/open-webui/pull/23209) +- 🧠 **Image viewer memory optimization.** Viewing images and SVGs now uses significantly less memory and performs faster, keeping the application snappy and responsive even when browsing through many media files during extended sessions. [#23236](https://github.com/open-webui/open-webui/pull/23236) +- 📡 **Optimized user activity tracking performance.** User activity updates now use a single database query instead of multiple operations, improving response times across all authenticated requests. [#23215](https://github.com/open-webui/open-webui/pull/23215) +- 👥 **Faster channel thread author loading.** Channel thread responses now load author details in a single batch query, reducing database overhead and improving responsiveness in threads with many participants. [#23795](https://github.com/open-webui/open-webui/pull/23795) +- 💨 **Optimized shared chat deletion.** Deleting shared chats by user is now faster and more memory-efficient by only loading necessary data. [#23216](https://github.com/open-webui/open-webui/pull/23216) +- 🗃️ **Faster chat tag loading.** Chat tag lookups now load only the metadata needed instead of full chat payloads, improving responsiveness for chats with large histories. [#23798](https://github.com/open-webui/open-webui/pull/23798) +- 📎 **Faster chat file deduplication.** Attaching files to chat messages now checks duplicates more efficiently, reducing overhead when handling larger file lists. [#23800](https://github.com/open-webui/open-webui/pull/23800) +- 📈 **Faster message diff checks.** Chat message and status updates now compare content more efficiently during streaming, making active conversations feel smoother and more responsive. [#23370](https://github.com/open-webui/open-webui/pull/23370) +- ⚖️ **Faster deep equality checks.** Chat message updates, model selection, note editing, code block refreshes, and rich text state comparisons now use deep equality checks that reduce unnecessary UI work and improve responsiveness in active sessions. [#23845](https://github.com/open-webui/open-webui/pull/23845) +- 🏃 **Faster knowledge access updates.** Updating access grants for knowledge items now completes with less backend overhead, making permission changes apply more quickly. [#23799](https://github.com/open-webui/open-webui/pull/23799) +- 🧹 **Mermaid render cleanup performance.** Mermaid diagrams now always clean up temporary render elements after failures, reducing DOM buildup and keeping repeated rendering more stable over time. [#23727](https://github.com/open-webui/open-webui/pull/23727) +- 🖼️ **Model image lookup efficiency.** Model profile image requests now reuse the current request database session, reducing per-request overhead and improving response efficiency. [#23796](https://github.com/open-webui/open-webui/pull/23796) +- 👤 **User endpoint query reduction.** Session-based user settings and status endpoints now avoid redundant user re-fetches, reducing unnecessary database load while preserving behavior. [#23794](https://github.com/open-webui/open-webui/pull/23794) +- 🚦 **Faster startup performance.** Open WebUI now checks for Torch MPS support only on macOS, avoiding unnecessary startup work on other platforms. [#23438](https://github.com/open-webui/open-webui/pull/23438) +- 🛡️ **Redis timeout consistency.** Redis connections now honor the "REDIS_SOCKET_CONNECT_TIMEOUT" setting across standard and cluster setups, helping workers fail faster when Redis is unreachable. [#23572](https://github.com/open-webui/open-webui/pull/23572) +- 🧰 **AIOHTTP pool controls.** Administrators can now tune shared outbound HTTP connection behavior with "AIOHTTP_POOL_CONNECTIONS", "AIOHTTP_POOL_CONNECTIONS_PER_HOST", and "AIOHTTP_POOL_DNS_TTL" for better control under high concurrency. [Commit](https://github.com/open-webui/open-webui/commit/c47dd7b7717c4186e0f0549ca3c8cb4d9bb38135) +- ⏱️ **MCP tool server timeout configuration.** Administrators can now configure request timeouts for MCP tool server connections via the AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER environment variable. [Commit](https://github.com/open-webui/open-webui/commit/10b4b86ada93cd62d994c3179ff14dfd1a6e56f0) +- 🎫 **Static OAuth tool authentication.** Tool server authentication now works reliably for both "oauth_2.1" and "oauth_2.1_static" connection types, so OAuth-backed tool access is correctly detected and forwarded during chat requests. [Commit](https://github.com/open-webui/open-webui/commit/60676bfdcfbce1a69b3e97f2013f0cfd63371737) +- 🗄️ **Configurable storage local cache.** Administrators can now disable persistent local caching for cloud-backed uploads with the "STORAGE_LOCAL_CACHE" setting, reducing local disk usage by cleaning temporary upload copies after processing. [Commit](https://github.com/open-webui/open-webui/commit/8172c7e3d56918d1372be06b9369b58a3a88f6b1) +- 🚪 **Back-channel logout.** OpenID Connect providers can now trigger centralized logout through the "ENABLE_OAUTH_BACKCHANNEL_LOGOUT" setting, helping administrators invalidate user sessions more reliably across connected devices. [Commit](https://github.com/open-webui/open-webui/commit/0dd9f462ffb2f160bc4aebad182047f41874d250) +- 🛡️ **Expanded security header controls.** Administrators can now configure additional browser security headers, including "CONTENT_SECURITY_POLICY_REPORT_ONLY", "CROSS_ORIGIN_EMBEDDER_POLICY", "CROSS_ORIGIN_OPENER_POLICY", and "CROSS_ORIGIN_RESOURCE_POLICY", for stricter and more flexible deployment hardening. [Commit](https://github.com/open-webui/open-webui/commit/f246a66810fa4995d9494da3599c0fb297fb0213) +- 🖼️ **Image MIME fallback option.** Administrators can now enable "ENABLE_IMAGE_CONTENT_TYPE_EXTENSION_FALLBACK" so image-to-base64 conversion can still detect common image types by file extension when MIME metadata is missing, improving compatibility on minimal container images and older file records. [Commit](https://github.com/open-webui/open-webui/commit/5127354b3eb4eaa71bc4ad68da69729e2196e7a4) +- 🛡️ **Public sharing permissions.** Public channels, models, notes, prompts, and tools now respect allowed access grants more consistently, helping administrators control who can share content more safely. [Commit](https://github.com/open-webui/open-webui/commit/9d3e0637c86292b8b92e7607097a83f1075d7cd8) +- 🆔 **Skill lookup by ID.** Skill instructions now include each skill’s ID, and the skill viewer now finds skills by ID in a case-insensitive way so attached skills are identified more reliably in chats. [Commit](https://github.com/open-webui/open-webui/commit/65ee771fd0d62d785ecbcf189e3f5b63858c11e6) +- 🏷️ **Source context metadata.** Retrieval source context now includes each source’s resource type and resource ID metadata, helping downstream model workflows preserve richer source identity during processing. [Commit](https://github.com/open-webui/open-webui/commit/c3c8c605d76a3b0ee067307f9cef6d081658e287) +- 🗂️ **Feedback filtering.** Administrators can now filter feedback history by model and export only the feedback they need. [Commit](https://github.com/open-webui/open-webui/commit/60e4d7517463690b3a87de38babc9ac561897c61) +- 📤 **CSV feedback export.** Feedback history can now be exported as either JSON or CSV, making it easier to analyze feedback in spreadsheet tools. [Commit](https://github.com/open-webui/open-webui/commit/342582676a5212bf196a69d11825cb407992f257) +- 📝 **Optional GET audit logging.** Administrators can now enable auditing for GET requests with the "ENABLE_AUDIT_GET_REQUESTS" setting when they need fuller request visibility. [Commit](https://github.com/open-webui/open-webui/commit/5ee791d5d28f236755243cb7d16d8737bb69ce36) +- 🕒 **Model access updates.** Changing a model’s access grants now updates its timestamp, so recently modified models stay easier to find and sort correctly. [Commit](https://github.com/open-webui/open-webui/commit/53eadb7df7281f5661cbe22c8b26b5aedaba3083) +- 💬 **Queued message handling.** Queued chat messages now send more reliably without advancing the queue too early, keeping follow-up prompts in the intended order. [Commit](https://github.com/open-webui/open-webui/commit/730e52a431d157dc62d72260668087437f1d52f4) +- 🔒 **Rendered content safety.** Placeholder descriptions and the pending account notice now render markdown with safer sanitization ordering, reducing the risk of unsafe HTML appearing in these views. [Commit](https://github.com/open-webui/open-webui/commit/253f416de3f2d3a939a6feef2a56413fd61cc70b) +- 🛡️ **Safer placeholder rendering.** Chat placeholder descriptions and the pending account notice now sanitize rendered markdown more consistently, reducing the risk of unsafe content being shown in these views. [Commit](https://github.com/open-webui/open-webui/commit/ae0316a30e01a2e5ff3f9d2f9f759c1cd6410f34) +- 🧮 **Usage analytics accuracy.** Token usage is now normalized before chat messages are saved, so model and user usage reports stay accurate across OpenAI-compatible providers. [Commit](https://github.com/open-webui/open-webui/commit/4dea4fdf54e00ebaba8e3178128bf8709453d2a2) +- 🧩 **Richer Anthropic tool results.** Anthropic-compatible tool calls now preserve more tool result content types, including images and structured search or document outputs, so models can use fuller tool context instead of receiving only plain text fragments. [#23188](https://github.com/open-webui/open-webui/issues/23188), [Commit](https://github.com/open-webui/open-webui/commit/40f5b3d135190dc9a2d8e94dbb1b2cbcbd829132) +- 🖼️ **ComfyUI request reliability.** ComfyUI image generation and editing now use shared async connections with consistent SSL handling, making image uploads and workflow runs more reliable under concurrent load. [Commit](https://github.com/open-webui/open-webui/commit/5944eda0ff25a284f7157252683bccede741cbe7) +- 🎛️ **Reranking batch size control.** Administrators can now set "RAG_RERANKING_BATCH_SIZE" in Documents settings to control reranking workload size, helping balance retrieval speed and resource usage for their deployment. [Commit](https://github.com/open-webui/open-webui/commit/4d2f18981051205016bd24d39521e25a33581225) +- 🔄 **General improvements.** Various improvements were implemented across the application to enhance performance, stability, and security. +- 🌐 **Translation updates.** Translations for Irish, Catalan, German, Simplified Chinese, Hindi, and Portuguese (Brazil) were enhanced and expanded. + +### Fixed + +- 🛡️ **Model description XSS protection.** Model descriptions shown in chat placeholders are now sanitized before rendering, preventing malicious links from executing scripts and helping protect user sessions from takeover. [#23621](https://github.com/open-webui/open-webui/pull/23621) +- 🧠 **Memory search filtering.** Memory search now correctly filters by the query text instead of returning unrelated results. [Commit](https://github.com/open-webui/open-webui/commit/43e5905c133049036353978704b0abd179716749), [#23826](https://github.com/open-webui/open-webui/issues/23826) +- 📊 **Shared chat analytics consistency.** Usage and message-count analytics now count assistant activity consistently across regular and shared chats, improving accuracy in model, user, chat, and time-based reporting views. [Commit](https://github.com/open-webui/open-webui/commit/e29d145a1cff23122de16123a4cfda1b84abffbb) +- 🧭 **Safer in-flight chat navigation.** Sending a message no longer overwrites your active chat or causes duplicate background notifications when you switch conversations before a response finishes. [Commit](https://github.com/open-webui/open-webui/commit/dc6df52a917b49fa1264ac81a8cc74603f6155b3) +- 🗣️ **Pipeline error detail visibility.** Pipeline inlet and outlet failures now preserve and surface provider error details more reliably in chat error messages, making troubleshooting failed requests much clearer. [Commit](https://github.com/open-webui/open-webui/commit/d5e69f182cd7a6371ab25248f6432b277f83ef23) +- 📨 **Shared chat event routing.** Message update and send events now target the chat owner’s event channel, so shared chats receive the correct real-time updates instead of routing events to the acting user. [Commit](https://github.com/open-webui/open-webui/commit/47329b5032ba29716a7e7e973b07c6d9894968e0) +- 🔐 **Consistent outbound SSL handling.** External requests for tools, functions, terminals, webhooks, retrieval loaders, audio provider discovery, and OpenAI-compatible embedding calls now consistently apply the configured SSL client setting, improving reliability for deployments that require custom certificate or verification behavior. [Commit](https://github.com/open-webui/open-webui/commit/fd25152076ea7c310e42c9bacc5cd2b544eeae48), [Commit](https://github.com/open-webui/open-webui/commit/56c5bc1d3487020ab886d3332aacc1644c1d6123) +- 🧭 **Scheduled Tasks calendar reliability.** Scheduled Tasks is now handled as a virtual automation calendar that appears only when automation access is available, and calendar selection now filters by stable ID instead of name so event forms behave consistently. [Commit](https://github.com/open-webui/open-webui/commit/1d501cfa3f96b3a9a5f4f7ce996947671fd09f29), [Commit](https://github.com/open-webui/open-webui/commit/24dd5b461eb44d306c823389e0f664c45db042e8) +- 🛡️ **Protected calendar deletion rules.** System and default calendars can no longer be deleted, preventing accidental removal of built-in calendar functionality. [Commit](https://github.com/open-webui/open-webui/commit/51627555bf356c8ec663f4d2f43f2f013eadbce4) +- 🖼️ **Image SSL setting support.** Image generation now respects the configured SSL session setting, preventing avoidable connection failures in strict certificate environments. [Commit](https://github.com/open-webui/open-webui/commit/128cf41fcedf2638fc8a6acd850d8b0409be1c4e), [#23777](https://github.com/open-webui/open-webui/issues/23777) +- 🗂️ **Folder ownership assignment hardening.** Folder create and update inputs now reject unexpected extra fields, preventing clients from overriding protected values like ownership through mass-assignment payloads. [#23648](https://github.com/open-webui/open-webui/pull/23648) +- 🔐 **Knowledge file deletion ownership checks.** Collaborators with knowledge base write access can no longer permanently delete files they do not own, preventing unintended file removal across other linked chats and knowledge bases. [Commit](https://github.com/open-webui/open-webui/commit/914ccf07ef158afe5588b97ed42778c93c439938), [#23636](https://github.com/open-webui/open-webui/pull/23636#issuecomment-4232439454) +- 🗑️ **Knowledge deletion reliability.** Deleting a knowledge base by ID now completes reliably without unexpected failures. [Commit](https://github.com/open-webui/open-webui/commit/7e453de4f7794ff386e285aa5951b94e926ec273), [#23776](https://github.com/open-webui/open-webui/issues/23776), [#23814](https://github.com/open-webui/open-webui/pull/23814) +- 🔐 **OAuth 2.1 PKCE enforcement.** OAuth 2.1 providers now default to S256 PKCE even when discovery metadata omits supported challenge methods, preventing login failures with providers that require PKCE by default. [#23667](https://github.com/open-webui/open-webui/issues/23667), [Commit](https://github.com/open-webui/open-webui/commit/050c4b97a95addc5eaeef86ba00631673a90dec4) +- 🔐 **Static OAuth scope handling.** Static OAuth credential flows now prioritize administrator-defined scopes and handle OAuth 2.1 static flow behavior more reliably. [Commit](https://github.com/open-webui/open-webui/commit/349ea4ea9e577f2cbfb4917ef5f52e5ac53c5b70), [#23668](https://github.com/open-webui/open-webui/issues/23668), [#23696](https://github.com/open-webui/open-webui/pull/23696), [#23783](https://github.com/open-webui/open-webui/pull/23783) +- 🔐 **Static OAuth tool registration reliability.** Static OAuth tool server registration now resolves and uses saved admin credentials more reliably, preventing registration failures when valid client credentials are provided. [#23670](https://github.com/open-webui/open-webui/issues/23670), [Commit](https://github.com/open-webui/open-webui/commit/2943955c529138c0e530fd07b6333a0052e3684e), [Commit](https://github.com/open-webui/open-webui/commit/c767bcaa739f76b1a4337dfd9d6be47adb504825) +- ⏳ **OAuth token expiry fallback.** OAuth sessions now always store a safe expiry value even when providers omit "expires_in" or "expires_at", so token refresh checks continue working and tool calls are less likely to fail later with unexpected authorization errors. [#23669](https://github.com/open-webui/open-webui/issues/23669), [Commit](https://github.com/open-webui/open-webui/commit/31406caa795173a59d5843d3601b891bf617cbaa) +- 🔑 **Anthropic x-api-key model access.** Anthropic-compatible clients can now authenticate with the "x-api-key" header across all relevant API routes, so model listing requests like GET "/api/v1/models" no longer fail with unauthorized errors. [#23319](https://github.com/open-webui/open-webui/issues/23319), [Commit](https://github.com/open-webui/open-webui/commit/611fe0c8a938539b73b559e84964f40c30bf436d) +- 🔑 **SSO password option visibility.** Account settings now hide password change controls when password-change access is disabled, avoiding misleading password options for SSO-focused setups. [#15292](https://github.com/open-webui/open-webui/issues/15292), [Commit](https://github.com/open-webui/open-webui/commit/cced77b584d6ea46c58fecddb2b3dd5e955c8417) +- 🔑 **Open Terminal MCP authentication.** Open Terminal MCP tool calls now include the configured API key when calling internal routes, preventing unauthorized errors for commands like file reads and command execution. [#106](https://github.com/open-webui/open-terminal/pull/106) +- 🧯 **Provider error freeze recovery.** Task-based chat requests now surface provider HTTP errors through normal failure handling, so content-filter and other upstream 4xx responses no longer leave chats stuck in a perpetual loading state. [#23663](https://github.com/open-webui/open-webui/issues/23663), [Commit](https://github.com/open-webui/open-webui/commit/96265cf042c8ab97dbec5d0efcce8010d0cd76e5) +- 🔄 **Immediate outlet filter updates.** Assistant messages modified by outlet filters now appear correctly as soon as streaming completes, without requiring a page refresh. [#23829](https://github.com/open-webui/open-webui/pull/23829) +- 🌊 **Middleware cancellation reliability.** Long-running requests now complete more reliably by preventing middleware-level cancellations from interrupting in-flight database and embedding work, reducing unexpected failures and noisy error logs when connections close early. [#23709](https://github.com/open-webui/open-webui/pull/23709) +- 🚦 **Async vector search responsiveness.** File processing, memory updates, and knowledge retrieval no longer block the server event loop during vector database operations, so other chats and requests stay responsive while indexing or search is running. [#23706](https://github.com/open-webui/open-webui/pull/23706) +- 🗒️ **Notes chat llama.cpp compatibility.** Notes AI chat no longer sends empty assistant prefill messages that can conflict with reasoning-enabled llama.cpp responses, preventing immediate 400 errors in Notes conversations. [Commit](https://github.com/open-webui/open-webui/commit/fd93bd3414a1725219e14561bc5640b62f9fd4a1), [#23703](https://github.com/open-webui/open-webui/issues/23703#issuecomment-4243907629) +- 🧩 **Ollama thinking field preservation.** Messages modified by filters now keep the Ollama "thinking" field when sent to the model, so reasoning-aware workflows and custom filter-based passthrough setups work reliably. [Commit](https://github.com/open-webui/open-webui/commit/8bd23b91459914eb7df5b5a66567d3544e0da168), [#22508](https://github.com/open-webui/open-webui/issues/22508) +- 🧾 **Reasoning content preservation.** Assistant tool-call messages now retain reasoning content across turns, improving reliability for reasoning-heavy model workflows. [Commit](https://github.com/open-webui/open-webui/commit/3dd8255816898467246c81cba3c9bc48bc18d86d), [#23175](https://github.com/open-webui/open-webui/issues/23175), [#23742](https://github.com/open-webui/open-webui/pull/23742) +- 🧭 **Background task scoping for new chats.** Chat title and auto-tag generation now run only for the first message of a new conversation and only once in multi-model responses, preventing duplicate or incorrectly triggered background tasks in follow-up flows. [Commit](https://github.com/open-webui/open-webui/commit/f102060a6d85db4acd3d0bf5c25e976f36cd5533..a4ed16999eec9a654a37c2bb4c15ba5ecd1fa3b7) +- 📚 **Channel document context retention.** Channel conversations now preserve and load the correct stored message history so model responses can use uploaded and retrieved document context more reliably. [#23686](https://github.com/open-webui/open-webui/issues/23686), [Commit](https://github.com/open-webui/open-webui/commit/cf4218e688def6f11d195aeda6665ae5b5376b67), [Commit](https://github.com/open-webui/open-webui/commit/18fe17127a7175579506e7456d3e5aba201371e6) +- ⏳ **Interrupted response recovery.** Assistant placeholder messages now start as incomplete and recover more safely after interrupted generations, preventing silent empty replies after refreshes or dropped requests. [#23176](https://github.com/open-webui/open-webui/issues/23176), [Commit](https://github.com/open-webui/open-webui/commit/c8ef7b028931263e8773cb60a7111d80d9572d26), [Commit](https://github.com/open-webui/open-webui/commit/cf4218e688def6f11d195aeda6665ae5b5376b67) +- 🧰 **Large tool result rendering.** Tool call details now display large result payloads reliably in chat instead of intermittently showing empty output for bigger tool responses. [#18743](https://github.com/open-webui/open-webui/issues/18743), [Commit](https://github.com/open-webui/open-webui/commit/45e49d33e51f7720c00b564215484aff9b48b20c) +- 🧼 **Null-byte document sanitization.** PDF and other document ingests now sanitize null bytes and invalid surrogate characters before pgvector writes, preventing PostgreSQL upload failures and allowing affected files to index successfully. [#22992](https://github.com/open-webui/open-webui/issues/22992), [Commit](https://github.com/open-webui/open-webui/commit/8dba798cce9fb1efc5f6acc5f37b152662db78d7) +- 📝 **Knowledge text editor stability.** The Knowledge "Add Text Content" modal now uses a plain text editor, avoiding current rich text editor issues and keeping drafting behavior consistent with existing knowledge editing flows. [Commit](https://github.com/open-webui/open-webui/commit/cd55c3e21237e000c13c6f396bb95b261f3bda82) +- 🎤 **STT SSL setting consistency.** Speech and related outbound media requests now consistently use shared async HTTP sessions and honor the configured SSL verification setting, improving compatibility with self-signed deployments. [#23672](https://github.com/open-webui/open-webui/issues/23672), [Commit](https://github.com/open-webui/open-webui/commit/2ddcb30b9a519885422ba1f36cc3485a7d897bf8) +- 🎙️ **Mistral speech input format.** Mistral speech-to-text requests now use the correct chat-completions audio input format for better compatibility. [Commit](https://github.com/open-webui/open-webui/commit/34d569d564a8ef2702c647dbad83eac840b76b2e), [#23822](https://github.com/open-webui/open-webui/issues/23822) +- 🖼️ **Optional image size parameter.** Image generation no longer sends the "size" field when no size is configured, improving compatibility with providers that reject unsupported size arguments. [#23611](https://github.com/open-webui/open-webui/issues/23611), [Commit](https://github.com/open-webui/open-webui/commit/869cf9e848b741705dc058550fa1b3f70db47fe8) +- 🔎 **FireCrawl timeout reliability.** FireCrawl web loading now uses direct scrape requests and improved timeout handling for single-URL fetches, reducing empty results and premature timeout failures with local FireCrawl setups. [#23411](https://github.com/open-webui/open-webui/issues/23411), [Commit](https://github.com/open-webui/open-webui/commit/9c64d84ad90804bf7d891e4a5097c03c4d7044c3) +- 🖱️ **Custom action icon drag prevention.** Custom user-added action icons in chat responses are no longer accidentally draggable, so clicks and hover interactions behave consistently with built-in action icons. [#23412](https://github.com/open-webui/open-webui/pull/23412) +- 🖼️ **Image URL conversion reliability.** Sending image URLs to AI models no longer fails with "cannot pickle 'coroutine' object" errors, so image inputs now convert to base64 reliably during request processing. [#23685](https://github.com/open-webui/open-webui/pull/23685#issuecomment-4240424635) +- 📂 **Channel input menu dismissal.** In Workspace Channels, the message input dropdown now closes immediately after selecting "Upload Files" or "Capture", matching normal chat input behavior and preventing the menu from staying open unnecessarily. [#23684](https://github.com/open-webui/open-webui/pull/23684) +- 📋 **Clipboard copy scroll stability.** Copying content with the fallback clipboard method no longer triggers unwanted page scrolling during focus, keeping your current reading position stable. [Commit](https://github.com/open-webui/open-webui/commit/fc98000aa8d439bbff21a70370f5e962bf23f4bc) +- 🖼️ **Profile image URL validation.** Profile saves now accept valid Open WebUI profile-image paths, trusted external HTTP(S) avatar URLs, and safe raster data-image formats while rejecting unsafe URL patterns that could be abused. [#23389](https://github.com/open-webui/open-webui/pull/23389) +- 👤 **Partial user profile updates.** User update API requests can now modify only the fields you provide, so administrators no longer need to resubmit unchanged name, email, and profile image values when changing a single setting like role. [#23424](https://github.com/open-webui/open-webui/issues/23424), [Commit](https://github.com/open-webui/open-webui/commit/3c2c611ba91d794a1e73134ec41b0de2b3927677) +- 🚨 **Provider SSE error visibility.** Provider failures returned with streaming content types are now surfaced as proper API errors and logged clearly, so issues like context-window limits no longer fail silently during chat generation. [#23379](https://github.com/open-webui/open-webui/pull/23379) +- 🧵 **Queued prompt race prevention.** Chat request queues now prevent overlapping processing for the same chat, avoiding duplicate queue handling when multiple queue-processing triggers fire close together. [#23181](https://github.com/open-webui/open-webui/issues/23181), [Commit](https://github.com/open-webui/open-webui/commit/e10a00132eed54a0108fb6ac120e8229deef3656) +- 🛑 **Cancellation event delivery reliability.** Cancelled chat processing now safely emits task-cancel and error events only when an event emitter is available, while provider HTTP errors now also route through task-cancel handling so chats recover from blocked-loading states more reliably. [#23663](https://github.com/open-webui/open-webui/issues/23663), [Commit](https://github.com/open-webui/open-webui/commit/51765b619c8584b042af68c3a5c87525a105ccd8), [Commit](https://github.com/open-webui/open-webui/commit/96265cf042c8ab97dbec5d0efcce8010d0cd76e5) +- 🔑 **OIDC key-rotation recovery.** OIDC login now retries token authorization with refreshed provider signing keys after a bad-signature failure, so logins recover automatically after identity-provider key rotation without requiring a service restart. [#23582](https://github.com/open-webui/open-webui/issues/23582), [Commit](https://github.com/open-webui/open-webui/commit/facb194a07486e847f0725a0a839e99b5864d37b) +- 🌍 **Non-ASCII tag filtering.** Prompt and model tag filters now handle non-Latin tags more reliably across SQLite and PostgreSQL, so tags like Cyrillic values return the expected items in Workspace lists. [#23381](https://github.com/open-webui/open-webui/issues/23381), [#23427](https://github.com/open-webui/open-webui/pull/23427), [Commit](https://github.com/open-webui/open-webui/commit/57784706e4fee75dec67e20b0d89a97351ac6256) +- 🏷️ **Prompt tag query accuracy.** Prompt tag filtering now uses JSON-element-aware queries so tag-based lookups return the correct prompts. [Commit](https://github.com/open-webui/open-webui/commit/e7e752f8e74e7b01fe2e6cb56f06e99312e1afe7), [#23386](https://github.com/open-webui/open-webui/pull/23386) +- 🗃️ **SQLite async pool compatibility.** SQLite async database setup no longer forces an explicit queue pool class, avoiding pool configuration conflicts in SQLite deployments. [Commit](https://github.com/open-webui/open-webui/commit/26b8ca5b5eeb144fae3fe6eaeae826150d8af826) +- 🧠 **Knowledge embedding deadlock prevention.** Knowledge file processing now runs blocking vector-save work in a worker thread while keeping async status updates reliable, preventing file processing from stalling during long embedding operations. [Commit](https://github.com/open-webui/open-webui/commit/d4b90f93bda2413ec8f040e61959acdb7b242061), [Commit](https://github.com/open-webui/open-webui/commit/22cfb3c673cbfa4a6bce26fde8e2e2754ce4963b) +- 🤖 **Automation worker async DB handling.** Automation claiming and run recording now use async database sessions consistently, improving worker stability for scheduled automations. [Commit](https://github.com/open-webui/open-webui/commit/cb6e77be3ec6ce00dd1f5b9ce3a655e6f65bc5da) +- 🕒 **Automation timezone scheduling.** Scheduled automations now calculate each user’s next run time using that user’s saved timezone, preventing run drift caused by server-time fallback. [Commit](https://github.com/open-webui/open-webui/commit/a4d62253df55c6307112eb76a6bfa29a7f538e21) +- 🔎 **Notes search matching.** Notes search now handles multi-word and hyphenated queries more reliably, so relevant notes and snippets are easier to find from partial phrase searches. [Commit](https://github.com/open-webui/open-webui/commit/a35926261646f8897ba71da1572ed5dff802e3be) +- 📐 **Display math rendering.** Chat markdown now correctly recognizes and renders "$$...$$" expressions as display math, improving reliability for multiline and escaped KaTeX content while keeping malformed delimiters from disrupting message rendering. [#23526](https://github.com/open-webui/open-webui/issues/23526), [Commit](https://github.com/open-webui/open-webui/commit/15b89b9218b7d2c7239c579aa3d23c2892227ac6) +- 🚫 **LDAP empty-password rejection.** LDAP login now rejects empty or whitespace-only passwords before bind attempts, preventing unauthenticated simple-bind behavior from granting access on permissive LDAP server configurations. [#23633](https://github.com/open-webui/open-webui/pull/23633) +- 🌐 **IPv6 SSRF address blocking.** URL validation now uses standard IP address checks for both IPv4 and IPv6, preventing private, loopback, link-local, reserved, and mapped-address SSRF bypasses through IPv6 hostname resolution. [#23453](https://github.com/open-webui/open-webui/pull/23453) +- 🔒 **API key endpoint restriction bypass.** API key endpoint restrictions are now enforced regardless of whether the key is sent through Authorization headers, cookies, or "x-api-key", preventing bypass through alternate key transport paths. [#23637](https://github.com/open-webui/open-webui/pull/23637) +- 🔐 **Channel sharing permission enforcement.** Channel creation and updates now enforce allowed access grant rules for public sharing, preventing unauthorized wildcard sharing on group channels. [#23638](https://github.com/open-webui/open-webui/pull/23638) +- 🛑 **Socket role invalidation.** Socket sessions now disconnect automatically when a user is demoted or deleted, preventing stale admin privileges from persisting until reconnect. [#23642](https://github.com/open-webui/open-webui/pull/23642) +- 🛂 **Tool server access checks.** Tool listing now correctly awaits server access checks, preventing users from seeing server-backed tools they do not have permission to use. [Commit](https://github.com/open-webui/open-webui/commit/d40f31982be3eed37e55e3f67b1eea9a5dc8c525) +- 🛑 **Task endpoint access control.** Global task listing and direct task stop endpoints are now restricted to administrators, while regular users can stop only their own chat tasks through a scoped chat endpoint. [#23454](https://github.com/open-webui/open-webui/pull/23454) +- 🧱 **Redis cache key isolation.** Tool server and terminal server cache entries now include the Redis key prefix, preventing multiple Open WebUI instances that share one Redis database from overwriting each other’s cached connection data. [#23649](https://github.com/open-webui/open-webui/pull/23649) +- 🧠 **Client session leak prevention.** Outbound provider requests now use a shared session pool with safer response cleanup and shutdown handling, preventing aiohttp session buildup and reducing memory growth during heavy concurrent API traffic. [#23540](https://github.com/open-webui/open-webui/issues/23540), [Commit](https://github.com/open-webui/open-webui/commit/c47dd7b7717c4186e0f0549ca3c8cb4d9bb38135) +- 🧩 **Tool enum value handling.** Tool schema generation now safely handles enum values as strings, preventing failures when OpenAPI parameters include non-string enum entries. [#23597](https://github.com/open-webui/open-webui/issues/23597), [Commit](https://github.com/open-webui/open-webui/commit/4498e6faf2b1bdd1caa0e2c1c15d90a2790cd721) +- 🧷 **Responses model access control.** The OpenAI-compatible Responses endpoint now enforces per-model permissions, preventing non-admin users from accessing models they are not allowed to use. [#23481](https://github.com/open-webui/open-webui/pull/23481) +- 🛡️ **Collection process endpoint permissions.** Collection processing endpoints now enforce collection ownership checks for web and text processing requests. [Commit](https://github.com/open-webui/open-webui/commit/ba83613ff297bc82db660b5273f04672d744902f), [#23634](https://github.com/open-webui/open-webui/pull/23634) +- 📚 **Knowledge query access enforcement.** Knowledge-base collection queries now block unauthorized enumeration and require read access before returning results. [Commit](https://github.com/open-webui/open-webui/commit/860b90fd17d14ba00674621edd294dee150491d2), [#23635](https://github.com/open-webui/open-webui/pull/23635), [#23452](https://github.com/open-webui/open-webui/pull/23452) +- 🔍 **RAG collection query permissions.** Vector search collection queries now enforce access checks before retrieval results are returned. [Commit](https://github.com/open-webui/open-webui/commit/f44b7a01f5b854f47c1594a1ab5f72096f736262), [#23627](https://github.com/open-webui/open-webui/pull/23627) +- 🔗 **Chained base model access checks.** Chained base model execution now enforces per-model access rules to prevent unauthorized model usage. [Commit](https://github.com/open-webui/open-webui/commit/8acce144f99992b75c25f0e5038b16881ce9f066), [Commit](https://github.com/open-webui/open-webui/commit/50363ba66b19613a2fc0cab6a3f7f724a825135e), [#23647](https://github.com/open-webui/open-webui/pull/23647) +- ✍️ **Collaborative document write checks.** Collaborative document updates now require proper write permission before changes are accepted. [Commit](https://github.com/open-webui/open-webui/commit/638c7ab80216452910bdc59a19eb90e6b7244c6c), [Commit](https://github.com/open-webui/open-webui/commit/3271b013a8b30a882364679dcb40ffc9a89f037e), [#23624](https://github.com/open-webui/open-webui/pull/23624) +- 📥 **Model import ownership validation.** Model import now enforces ownership and access grant checks to prevent unauthorized imports. [Commit](https://github.com/open-webui/open-webui/commit/499129625bf96b2c03a6d057a2f91fdf07fd1c49), [#23628](https://github.com/open-webui/open-webui/pull/23628) +- 🚫 **Inactive member channel access.** Deactivated group members can no longer read or write channel content through direct API calls, so channel permissions now match active membership status. [#23623](https://github.com/open-webui/open-webui/pull/23623) +- 🎛️ **Ollama endpoint model permissions.** Restricted models are now protected on Ollama show, generate, embed, and embeddings endpoints, preventing authenticated users from using private models without read access. [#23631](https://github.com/open-webui/open-webui/pull/23631) +- 🧭 **Azure deployment path validation.** Azure model names are now validated and safely encoded before request URL construction, preventing path traversal attempts from reaching unintended Azure endpoints. [#23629](https://github.com/open-webui/open-webui/pull/23629) +- 👥 **Private channel member list access.** Standard channel member lists now require proper read permission, preventing unauthorized users from enumerating members of private channels by direct API calls. [#23625](https://github.com/open-webui/open-webui/pull/23625) +- 🌀 **Tool server schema recursion safety.** Tool server OpenAPI conversion now handles circular request schema references safely, preventing conversion crashes and ensuring one bad tool server spec does not break the full tool server list. [#23588](https://github.com/open-webui/open-webui/pull/23588), [Commit](https://github.com/open-webui/open-webui/commit/d3df8f1f372411314be9121fbf61d107939fa258) +- 🧱 **Safer file path handling.** File upload, transcription cache, and model download paths now use safer path construction helpers to reduce path parsing risks and improve cross-platform path safety. [Commit](https://github.com/open-webui/open-webui/commit/15f9a8f3f13f112c96cb1b16f88859f65de58346) +- 🧾 **Prompt save error feedback.** Saving prompt edits now shows a clear error toast if the save fails, so failed updates are visible instead of silently failing in the editor flow. [Commit](https://github.com/open-webui/open-webui/commit/36a81ad43b7c0d450079f818a7546eaa517e3d95) +- 🧾 **Tool call JSON rendering.** Tool call arguments and structured results now render as plain formatted JSON blocks instead of markdown code fences, preventing formatting quirks and making tool output easier to read consistently. [Commit](https://github.com/open-webui/open-webui/commit/a7d4c53f3adb80768b67e4a410b486b04a581521) +- 👥 **First-user admin race protection.** Concurrent first-time LDAP or OAuth registrations can no longer create multiple admin accounts, so only the true first account is promoted during initial setup. [#23626](https://github.com/open-webui/open-webui/pull/23626) +- 🔒 **SCIM token checks.** SCIM authentication now compares tokens in a safer way, helping prevent timing-based token guessing attacks. [#23577](https://github.com/open-webui/open-webui/pull/23577) +- 🔒 **Safer file access checks.** HTML file previews now treat missing or non-admin owners as inaccessible, preventing accidental access to files that should not be shown. [Commit](https://github.com/open-webui/open-webui/commit/6acaaea59a50ec26da03e6144017a2fd86241ce9) +- 🖼️ **ComfyUI request hangs.** Concurrent image generation and editing requests to ComfyUI now complete reliably instead of getting stuck when the same user starts multiple requests at once. [#23592](https://github.com/open-webui/open-webui/pull/23592), [#23591](https://github.com/open-webui/open-webui/issues/23591) +- 🧭 **Permission-aware built-in tools.** Built-in tools now consistently respect user feature permissions for memories, web search, image generation, code interpreter, notes, channels, and automations, preventing tools from being exposed to users without access. [Commit](https://github.com/open-webui/open-webui/commit/588b81eedaacbfd7394b707ae1600d9fb729b809..674695918e5e3e1811314ce2a082c5bbb42d76b2) +- 🛑 **Interrupted MCP cleanup stability.** Interrupted MCP tool calls no longer leave runaway cleanup behavior that can drive container CPU usage to 100%, keeping instances stable after cancellations or dropped connections. [#23143](https://github.com/open-webui/open-webui/issues/23143) +- 🚪 **OAuth redirect URI reliability.** OAuth login redirects now use provider client metadata more consistently, preventing incorrect HTTP callback URLs behind reverse proxies and improving sign-in reliability for providers such as Feishu. [#23203](https://github.com/open-webui/open-webui/pull/23203), [#23128](https://github.com/open-webui/open-webui/issues/23128) +- 🌐 **OAuth redirect handling.** OAuth provider token exchange now follows redirects automatically, improving sign-in reliability with identity providers that redirect token endpoint requests. [#23409](https://github.com/open-webui/open-webui/issues/23409), [Commit](https://github.com/open-webui/open-webui/commit/498ff8cdc3dd47000cdc60e5adcf36f4adfbe07d) +- ☁️ **OneDrive picker redirect handling.** OneDrive file picker authentication now uses the current app origin as the redirect URI, improving sign-in reliability when launching the picker from deployed environments. [#23450](https://github.com/open-webui/open-webui/issues/23450), [Commit](https://github.com/open-webui/open-webui/commit/21cc8281323d505d7d084cc496bd433063315c86) +- 🍪 **OAuth session cookie persistence.** OIDC sign-in now correctly sets the "oauth_session_id" cookie, so "system_oauth" connections can forward user OAuth tokens to upstream providers as expected. [#23251](https://github.com/open-webui/open-webui/pull/23251), [#23250](https://github.com/open-webui/open-webui/issues/23250) +- 🔑 **OAuth session cookie handling.** OAuth callback processing no longer fails on undefined cookie expiry data, so OAuth session cookies are stored correctly after sign-in. [#23207](https://github.com/open-webui/open-webui/pull/23207), [#23197](https://github.com/open-webui/open-webui/issues/23197) +- 🔏 **Ollama SSL handling.** Ollama model management and file uploads now respect the configured SSL verification setting, so self-signed certificates work when SSL verification is disabled. [#23503](https://github.com/open-webui/open-webui/issues/23503), [Commit](https://github.com/open-webui/open-webui/commit/e51b661af0e71a24f041428f328fcc6e97a15262) +- 🛡️ **OAuth avatar URL validation.** OAuth sign-in now validates profile picture URLs before fetching them, preventing invalid image links from causing login-time errors. [#23356](https://github.com/open-webui/open-webui/pull/23356) +- 🔑 **User invite token expiry.** New user invite logins now respect the configured "JWT_EXPIRES_IN" setting, so signup tokens expire as expected instead of using the default lifetime. [#23576](https://github.com/open-webui/open-webui/pull/23576) +- 🚪 **Channel access checks.** Channel actions now verify the current user when checking access, improving permission enforcement across channel views and message actions. [Commit](https://github.com/open-webui/open-webui/commit/4632f200a9ac98c915aee412b34e86c3d3c58bb1) +- 📣 **Channel message lookups.** Channel message details and pinning now work more reliably when the sender account is missing, avoiding failures in those views. [Commit](https://github.com/open-webui/open-webui/commit/6acaaea59a50ec26da03e6144017a2fd86241ce9) +- 📌 **Pinned webhook message handling.** Viewing pinned webhook messages now works reliably even when webhook profile data is missing, preventing server errors and frontend crashes in channel pinned message dialogs. [#23414](https://github.com/open-webui/open-webui/pull/23414) +- 🛡️ **Note edit permission enforcement.** Note saving now requires write access instead of read access, preventing unauthorized users from modifying notes while preserving expected collaboration permissions. [Commit](https://github.com/open-webui/open-webui/commit/584a9a0920d8c8c72fc89ccbac83c970b5a4bd4a) +- 🗂️ **Archived chats menu visibility.** The 'Archived Chats' option in the user menu is now shown reliably for all users, so non-admin accounts can consistently access archived conversations. [Commit](https://github.com/open-webui/open-webui/commit/07262fa62c2323fc7948389e5b5b8a5d1b72fade) +- 💾 **Error message persistence.** LLM errors that occur during streaming are now saved to the database even if the connection drops, so users can see what went wrong when they reconnect. [#23231](https://github.com/open-webui/open-webui/pull/23231) +- 🚫 **Missing message completion guard.** Chat completion finalization now skips invalid requests without a message identifier, preventing unnecessary error toasts caused by rare frontend concurrency timing. [#23184](https://github.com/open-webui/open-webui/pull/23184) +- 🧠 **Active message completion accuracy.** Switching chats or refreshing during generation no longer marks the currently streaming assistant message as finished too early, so thinking blocks and action buttons appear at the correct time. [#23171](https://github.com/open-webui/open-webui/issues/23171) +- 📞 **Call overlay visibility.** Incoming call events now open the call overlay and controls reliably, preventing cases where the call interface briefly appeared and then disappeared. [Commit](https://github.com/open-webui/open-webui/commit/ee9db91df02120e1e3651e8881734966b710ad52) +- 💬 **Prompt submission handling.** Chat messages now preserve attached files more reliably when prompts are sent, including queued messages and shared prompt actions. [Commit](https://github.com/open-webui/open-webui/commit/6d6dfbf02c893d72d85d4490cb41f1665b1f9f95) +- 🧾 **Prompt variable form saving.** Prompt variable forms now save reliably without runtime errors or an unresponsive save action, so input values and placeholders work correctly when applying prompt templates with variables. [#23225](https://github.com/open-webui/open-webui/issues/23225), [#23480](https://github.com/open-webui/open-webui/issues/23480) +- 🛟 **Task model fallback safety.** Task routing now handles missing default model entries safely, preventing task execution failures when the previously selected model is no longer available. [#23169](https://github.com/open-webui/open-webui/pull/23169) +- 📊 **Usage statistic preservation.** Follow-up generation no longer overwrites existing token usage fields, so stored usage statistics remain accurate for the main response. [#23152](https://github.com/open-webui/open-webui/issues/23152) +- 📝 **Writing block parsing reliability.** ":::writing" blocks now parse more reliably when headers or extra inline text are present, preventing malformed rendering and duplicate output artifacts. [#23174](https://github.com/open-webui/open-webui/issues/23174) +- 🧾 **Code block line break reliability.** Blank lines in submitted code blocks are now preserved more reliably instead of being collapsed. [Commit](https://github.com/open-webui/open-webui/commit/1be9627dd27ffe75957729a4a0d1682a98684f01), [#20302](https://github.com/open-webui/open-webui/issues/20302), [#23451](https://github.com/open-webui/open-webui/pull/23451) +- ✂️ **Citation spacing cleanup.** When citations are disabled for a model, citation markers and their leftover spacing are now removed together so punctuation and copied text remain cleanly formatted. [#23141](https://github.com/open-webui/open-webui/issues/23141) +- 🧰 **Pipe tool access.** Pipe functions now receive built-in and MCP tools in __tools__, so tools like Web Search and code execution are available when enabled. [#23365](https://github.com/open-webui/open-webui/issues/23365) +- 📚 **Batch file processing database handling.** Batch knowledge file processing now consistently uses the active database session, preventing failures caused by missing database context during file ownership checks and update writes. [#23137](https://github.com/open-webui/open-webui/issues/23137) +- ⚙️ **Default model parameter loading.** The "DEFAULT_MODEL_PARAMS" environment variable is now parsed and applied correctly, so default generation settings are honored reliably without being ignored at startup. [#23223](https://github.com/open-webui/open-webui/pull/23223) +- 🔧 **Web search settings save reliability.** Saving web search configuration now works without server errors, so administrators can update "WEB_FETCH_MAX_CONTENT_LENGTH" and related retrieval settings successfully from the admin interface. [Commit](https://github.com/open-webui/open-webui/commit/36d02aa1477aa1b4e7fb59d022f99693ebfa8667), [#23127](https://github.com/open-webui/open-webui/issues/23127) +- 🔍 **Web search result count.** The built-in search_web tool now respects the admin-configured "Search Result Count" setting instead of always returning 5 results when using Native Function Calling mode. [#23488](https://github.com/open-webui/open-webui/pull/23488), [#23485](https://github.com/open-webui/open-webui/issues/23485) +- 🖼️ **Open Terminal file response handling.** Open Terminal tool responses now preserve binary content types in user-side connections, so image and non-text file reads work consistently instead of being forced into plain text. [#23125](https://github.com/open-webui/open-webui/issues/23125) +- 🖥️ **Terminal label casing.** Terminal names in the chat input now display exactly as stored instead of being automatically capitalized, so domain-style server names appear correctly. [#23518](https://github.com/open-webui/open-webui/pull/23518) +- 🖼️ **Gravatar profile photo saving.** Gravatar profile images can now be saved successfully from account settings, with clearer validation and error handling instead of failing with generic object errors. [#23156](https://github.com/open-webui/open-webui/issues/23156) +- 🪟 **Details expansion preference.** Tool call detail groups now honor the 'Always Expand Details' chat setting, so they open expanded by default when that preference is enabled. [#23262](https://github.com/open-webui/open-webui/pull/23262), [#23255](https://github.com/open-webui/open-webui/issues/23255) +- 🖱️ **Rapid sidebar action protection.** Archive and delete actions in the chat sidebar now ignore repeated clicks while a request is in progress, preventing duplicate requests and stacked error toasts. [#23172](https://github.com/open-webui/open-webui/issues/23172) +- 📲 **Mobile model selector positioning.** The mobile model selector dropdown now applies a constrained viewport width and left offset, preventing overflow and making model selection easier on small screens. [#23310](https://github.com/open-webui/open-webui/pull/23310) +- 🔽 **Task list toggle icons.** The task list collapse button now shows the correct arrow direction, making task sections easier to expand and collapse at a glance. [Commit](https://github.com/open-webui/open-webui/commit/f66b67c8b86b6f9d896a23c7bb53907c2e6b15d3), [#23354](https://github.com/open-webui/open-webui/issues/23354) +- ➕ **Attachment menu auto-close.** The chat attachment menu now closes immediately after selecting upload actions like file upload, camera capture, web attach, Google Drive, or OneDrive, preventing the menu from lingering on screen. [Commit](https://github.com/open-webui/open-webui/commit/4764dd5d3765c22384ed38cbc97a8170daa7a75f), [#23320](https://github.com/open-webui/open-webui/issues/23320) +- 🧹 **Per-chat draft clearing.** Sent message drafts are now cleared using the active chat key, so sent text no longer reappears in the input after a refresh. [Commit](https://github.com/open-webui/open-webui/commit/124b7e9154d7f3ca8a16f2b90621209ac8d6b8c1), [#23296](https://github.com/open-webui/open-webui/issues/23296) +- ✉️ **Context-aware input action button.** The input now shows the send action when text or files are present during generation, while keeping stop controls for truly empty input states to avoid action confusion. [Commit](https://github.com/open-webui/open-webui/commit/86472bb4453af7ea4e5ddc8d127b14d8e67733bc), [#23306](https://github.com/open-webui/open-webui/issues/23306) +- 📉 **Pyodide prompt cache stability.** Pyodide code interpreter context is now appended to the system prompt instead of user messages, preserving stable prefix caching across turns and reducing repeated token costs in long native tool-calling chats. [#23269](https://github.com/open-webui/open-webui/issues/23269) +- 🧪 **Temp chat outlet filtering.** Outlet filters now process temporary chats more reliably, preserving assistant output and usage data so local chat responses stay consistent when filter pipelines are enabled. [Commit](https://github.com/open-webui/open-webui/commit/70a6a24f143b221c787bc50b72582ee1e0c2dac0) + +### Changed + +- ⚠️ **Database Migrations**: This release includes database schema changes; we strongly recommend backing up your database and all associated data before upgrading in production environments. If you are running a multi-worker, multi-server, or load-balanced deployment, all instances must be updated simultaneously, rolling updates are not supported and will cause application failures due to schema incompatibility. +- 🧨 **Plugin async migration required.** Custom plugins for Tools, Functions, and Pipelines may require migration to the new async backend signatures after upgrading, so plugin maintainers should update handlers and database call patterns for compatibility and follow the 0.9.0 plugin migration guide. [Migration Guide](https://docs.openwebui.com/features/extensibility/plugin/migration/to-0.9.0) +- 🔄 **Automation terminal source.** Automations now use the terminal configured on the selected model instead of a separate per-automation terminal picker, keeping terminal behavior consistent between chat and scheduled runs. [Commit](https://github.com/open-webui/open-webui/commit/47d413ce7b2a006a8126f4a9055b13e5fcb33a1d) +- 🚧 **OpenAI passthrough now opt-in.** Direct OpenAI catch-all proxy requests are now disabled by default and require enabling "ENABLE_OPENAI_API_PASSTHROUGH", so deployments relying on passthrough must explicitly turn it on after upgrading. [#23640](https://github.com/open-webui/open-webui/pull/23640) +- 🗄️ **SQLite WAL default enabled.** SQLite deployments now default to enabling write-ahead logging, improving concurrent read and write behavior without requiring manual configuration. [Commit](https://github.com/open-webui/open-webui/commit/2f9e326dba3b1087932cb6b8075ed1881bd1c6d6) + ## [0.8.12] - 2026-03-26 ### Added From 5f76c250f880d07ebc9b856b604cc1b8029f7eb4 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 15:20:28 +0900 Subject: [PATCH 323/334] refac --- src/lib/components/chat/MessageInput/TerminalMenu.svelte | 2 +- src/lib/components/chat/SettingsModal.svelte | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/lib/components/chat/MessageInput/TerminalMenu.svelte b/src/lib/components/chat/MessageInput/TerminalMenu.svelte index 8aaf880c90..ca721ceb2d 100644 --- a/src/lib/components/chat/MessageInput/TerminalMenu.svelte +++ b/src/lib/components/chat/MessageInput/TerminalMenu.svelte @@ -125,7 +125,7 @@ class="p-0.5 rounded-md text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition" on:click|stopPropagation={() => { show = false; - showSettings.set(true); + showSettings.set('tools'); }} > Date: Tue, 21 Apr 2026 15:41:07 +0900 Subject: [PATCH 324/334] refac --- backend/open_webui/routers/channels.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/routers/channels.py b/backend/open_webui/routers/channels.py index 22feb1b8f6..487899fccf 100644 --- a/backend/open_webui/routers/channels.py +++ b/backend/open_webui/routers/channels.py @@ -305,7 +305,7 @@ async def create_new_channel( detail=ERROR_MESSAGES.UNAUTHORIZED, ) - form_data.access_grants = filter_allowed_access_grants( + form_data.access_grants = await filter_allowed_access_grants( request.app.state.config.USER_PERMISSIONS, user.id, user.role, @@ -643,7 +643,7 @@ async def update_channel_by_id( if channel.user_id != user.id and user.role != 'admin': raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()) - form_data.access_grants = filter_allowed_access_grants( + form_data.access_grants = await filter_allowed_access_grants( request.app.state.config.USER_PERMISSIONS, user.id, user.role, From b9fc3f367ae739a0c9364417c1be31681e0b237b Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 15:47:32 +0900 Subject: [PATCH 325/334] refac --- backend/open_webui/retrieval/utils.py | 117 +++++++++++++++++++++++- backend/open_webui/routers/retrieval.py | 38 +------- 2 files changed, 117 insertions(+), 38 deletions(-) diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index 93ba72ce13..cafb8fe4f0 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -83,11 +83,120 @@ def get_loader(request, url: str): ) +def build_loader_from_config(request): + """Build a Loader instance with the admin's configured extraction engine settings.""" + from open_webui.retrieval.loaders.main import Loader + + config = request.app.state.config + return Loader( + engine=config.CONTENT_EXTRACTION_ENGINE, + DATALAB_MARKER_API_KEY=config.DATALAB_MARKER_API_KEY, + DATALAB_MARKER_API_BASE_URL=config.DATALAB_MARKER_API_BASE_URL, + DATALAB_MARKER_ADDITIONAL_CONFIG=config.DATALAB_MARKER_ADDITIONAL_CONFIG, + DATALAB_MARKER_SKIP_CACHE=config.DATALAB_MARKER_SKIP_CACHE, + DATALAB_MARKER_FORCE_OCR=config.DATALAB_MARKER_FORCE_OCR, + DATALAB_MARKER_PAGINATE=config.DATALAB_MARKER_PAGINATE, + DATALAB_MARKER_STRIP_EXISTING_OCR=config.DATALAB_MARKER_STRIP_EXISTING_OCR, + DATALAB_MARKER_DISABLE_IMAGE_EXTRACTION=config.DATALAB_MARKER_DISABLE_IMAGE_EXTRACTION, + DATALAB_MARKER_FORMAT_LINES=config.DATALAB_MARKER_FORMAT_LINES, + DATALAB_MARKER_USE_LLM=config.DATALAB_MARKER_USE_LLM, + DATALAB_MARKER_OUTPUT_FORMAT=config.DATALAB_MARKER_OUTPUT_FORMAT, + EXTERNAL_DOCUMENT_LOADER_URL=config.EXTERNAL_DOCUMENT_LOADER_URL, + EXTERNAL_DOCUMENT_LOADER_API_KEY=config.EXTERNAL_DOCUMENT_LOADER_API_KEY, + TIKA_SERVER_URL=config.TIKA_SERVER_URL, + DOCLING_SERVER_URL=config.DOCLING_SERVER_URL, + DOCLING_API_KEY=config.DOCLING_API_KEY, + DOCLING_PARAMS=config.DOCLING_PARAMS, + PDF_EXTRACT_IMAGES=config.PDF_EXTRACT_IMAGES, + PDF_LOADER_MODE=config.PDF_LOADER_MODE, + DOCUMENT_INTELLIGENCE_ENDPOINT=config.DOCUMENT_INTELLIGENCE_ENDPOINT, + DOCUMENT_INTELLIGENCE_KEY=config.DOCUMENT_INTELLIGENCE_KEY, + DOCUMENT_INTELLIGENCE_MODEL=config.DOCUMENT_INTELLIGENCE_MODEL, + MISTRAL_OCR_API_BASE_URL=config.MISTRAL_OCR_API_BASE_URL, + MISTRAL_OCR_API_KEY=config.MISTRAL_OCR_API_KEY, + MINERU_API_MODE=config.MINERU_API_MODE, + MINERU_API_URL=config.MINERU_API_URL, + MINERU_API_KEY=config.MINERU_API_KEY, + MINERU_API_TIMEOUT=config.MINERU_API_TIMEOUT, + MINERU_PARAMS=config.MINERU_PARAMS, + ) + + +def _extract_text_from_binary_response( + request, response: requests.Response, url: str +) -> tuple[str, list]: + """Download response body to a temp file and extract text using the Loader pipeline.""" + import mimetypes + import tempfile + import urllib.parse + + content_type = response.headers.get('Content-Type', '').split(';')[0].strip() + + # Derive filename from URL path, falling back to Content-Disposition or mime guess + url_path = urllib.parse.urlparse(url).path + filename = os.path.basename(url_path) if url_path else '' + + if not filename or '.' not in filename: + # Try Content-Disposition header + cd = response.headers.get('Content-Disposition', '') + if 'filename=' in cd: + filename = cd.split('filename=')[-1].strip('"\'') + + if not filename or '.' not in filename: + ext = mimetypes.guess_extension(content_type) or '' + filename = f'download{ext}' + + suffix = '.' + filename.split('.')[-1].lower() if '.' in filename else '' + + with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp: + tmp.write(response.content) + tmp_path = tmp.name + + try: + loader = build_loader_from_config(request) + docs = loader.load(filename, content_type, tmp_path) + for doc in docs: + doc.metadata['source'] = url + content = ' '.join([doc.page_content for doc in docs]) + return content, docs + finally: + os.remove(tmp_path) + + +def _is_text_content_type(content_type: str) -> bool: + """Return True if the content type should be handled by the web loader.""" + ct = content_type.split(';')[0].strip().lower() + if ct.startswith('text/'): + return True + if any(t in ct for t in ['xml', 'json', 'javascript']): + return True + return not ct # empty / missing → assume HTML + + def get_content_from_url(request, url: str) -> str: - loader = get_loader(request, url) - docs = loader.load() - content = ' '.join([doc.page_content for doc in docs]) - return content, docs + # Streamed GET to check Content-Type without downloading the body. + try: + response = requests.get(url, stream=True, timeout=30) + response.raise_for_status() + content_type = response.headers.get('Content-Type', '') + except Exception: + content_type = '' + response = None + + # Text / HTML / unknown — use the configured web loader + if response is None or _is_text_content_type(content_type): + if response is not None: + response.close() + loader = get_loader(request, url) + docs = loader.load() + content = ' '.join([doc.page_content for doc in docs]) + return content, docs + + # Binary content (PDF, DOCX, XLSX, PPTX, etc.) — download and extract + try: + return _extract_text_from_binary_response(request, response, url) + finally: + response.close() CHUNK_HASH_KEY = '_chunk_hash' diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index ee8a9007fe..fea00143e6 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -48,7 +48,7 @@ from open_webui.retrieval.vector.factory import VECTOR_DB_CLIENT from open_webui.retrieval.vector.async_client import ASYNC_VECTOR_DB_CLIENT # Document loaders -from open_webui.retrieval.loaders.main import Loader + from open_webui.retrieval.loaders.youtube import YoutubeLoader # Web search engines @@ -82,6 +82,7 @@ from open_webui.retrieval.web.yandex import search_yandex from open_webui.retrieval.web.ydc import search_youcom from open_webui.retrieval.utils import ( + build_loader_from_config, filter_accessible_collections, get_content_from_url, get_embedding_function, @@ -1623,39 +1624,8 @@ async def process_file( file_path = file.path if file_path: file_path = await asyncio.to_thread(Storage.get_file, file_path) - loader = Loader( - engine=request.app.state.config.CONTENT_EXTRACTION_ENGINE, - user=user, - DATALAB_MARKER_API_KEY=request.app.state.config.DATALAB_MARKER_API_KEY, - DATALAB_MARKER_API_BASE_URL=request.app.state.config.DATALAB_MARKER_API_BASE_URL, - DATALAB_MARKER_ADDITIONAL_CONFIG=request.app.state.config.DATALAB_MARKER_ADDITIONAL_CONFIG, - DATALAB_MARKER_SKIP_CACHE=request.app.state.config.DATALAB_MARKER_SKIP_CACHE, - DATALAB_MARKER_FORCE_OCR=request.app.state.config.DATALAB_MARKER_FORCE_OCR, - DATALAB_MARKER_PAGINATE=request.app.state.config.DATALAB_MARKER_PAGINATE, - DATALAB_MARKER_STRIP_EXISTING_OCR=request.app.state.config.DATALAB_MARKER_STRIP_EXISTING_OCR, - DATALAB_MARKER_DISABLE_IMAGE_EXTRACTION=request.app.state.config.DATALAB_MARKER_DISABLE_IMAGE_EXTRACTION, - DATALAB_MARKER_FORMAT_LINES=request.app.state.config.DATALAB_MARKER_FORMAT_LINES, - DATALAB_MARKER_USE_LLM=request.app.state.config.DATALAB_MARKER_USE_LLM, - DATALAB_MARKER_OUTPUT_FORMAT=request.app.state.config.DATALAB_MARKER_OUTPUT_FORMAT, - EXTERNAL_DOCUMENT_LOADER_URL=request.app.state.config.EXTERNAL_DOCUMENT_LOADER_URL, - EXTERNAL_DOCUMENT_LOADER_API_KEY=request.app.state.config.EXTERNAL_DOCUMENT_LOADER_API_KEY, - TIKA_SERVER_URL=request.app.state.config.TIKA_SERVER_URL, - DOCLING_SERVER_URL=request.app.state.config.DOCLING_SERVER_URL, - DOCLING_API_KEY=request.app.state.config.DOCLING_API_KEY, - DOCLING_PARAMS=request.app.state.config.DOCLING_PARAMS, - PDF_EXTRACT_IMAGES=request.app.state.config.PDF_EXTRACT_IMAGES, - PDF_LOADER_MODE=request.app.state.config.PDF_LOADER_MODE, - DOCUMENT_INTELLIGENCE_ENDPOINT=request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT, - DOCUMENT_INTELLIGENCE_KEY=request.app.state.config.DOCUMENT_INTELLIGENCE_KEY, - DOCUMENT_INTELLIGENCE_MODEL=request.app.state.config.DOCUMENT_INTELLIGENCE_MODEL, - MISTRAL_OCR_API_BASE_URL=request.app.state.config.MISTRAL_OCR_API_BASE_URL, - MISTRAL_OCR_API_KEY=request.app.state.config.MISTRAL_OCR_API_KEY, - MINERU_API_MODE=request.app.state.config.MINERU_API_MODE, - MINERU_API_URL=request.app.state.config.MINERU_API_URL, - MINERU_API_KEY=request.app.state.config.MINERU_API_KEY, - MINERU_API_TIMEOUT=request.app.state.config.MINERU_API_TIMEOUT, - MINERU_PARAMS=request.app.state.config.MINERU_PARAMS, - ) + loader = build_loader_from_config(request) + loader.user = user docs = await loader.aload(file.filename, file.meta.get('content_type'), file_path) docs = [ From 6cc799b1bbc77a3ec3d1484abd0d95b41a5baca7 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 15:52:00 +0900 Subject: [PATCH 326/334] chore: format --- CHANGELOG.md | 2 +- backend/open_webui/internal/db.py | 20 +++++------ backend/open_webui/models/calendar.py | 2 -- backend/open_webui/retrieval/utils.py | 4 +-- backend/open_webui/routers/calendar.py | 4 +-- backend/open_webui/routers/configs.py | 16 ++++++--- backend/open_webui/routers/functions.py | 4 ++- backend/open_webui/routers/tools.py | 4 ++- backend/open_webui/utils/files.py | 36 ++++++++----------- backend/open_webui/utils/tools.py | 8 +++-- .../calendar/CalendarSidebar.svelte | 11 +++--- src/lib/components/chat/Chat.svelte | 13 ++----- src/lib/components/chat/MessageInput.svelte | 4 ++- src/lib/i18n/locales/ar-BH/translation.json | 23 ++++++++++++ src/lib/i18n/locales/ar/translation.json | 23 ++++++++++++ src/lib/i18n/locales/az-AZ/translation.json | 19 ++++++++++ src/lib/i18n/locales/bg-BG/translation.json | 19 ++++++++++ src/lib/i18n/locales/bn-BD/translation.json | 19 ++++++++++ src/lib/i18n/locales/bo-TB/translation.json | 18 ++++++++++ src/lib/i18n/locales/bs-BA/translation.json | 20 +++++++++++ src/lib/i18n/locales/ca-ES/translation.json | 20 +++++++++++ src/lib/i18n/locales/ceb-PH/translation.json | 19 ++++++++++ src/lib/i18n/locales/cs-CZ/translation.json | 21 +++++++++++ src/lib/i18n/locales/da-DK/translation.json | 19 ++++++++++ src/lib/i18n/locales/de-DE/translation.json | 19 ++++++++++ src/lib/i18n/locales/dg-DG/translation.json | 19 ++++++++++ src/lib/i18n/locales/el-GR/translation.json | 19 ++++++++++ src/lib/i18n/locales/en-GB/translation.json | 19 ++++++++++ src/lib/i18n/locales/en-US/translation.json | 19 ++++++++++ src/lib/i18n/locales/es-ES/translation.json | 20 +++++++++++ src/lib/i18n/locales/et-EE/translation.json | 19 ++++++++++ src/lib/i18n/locales/eu-ES/translation.json | 19 ++++++++++ src/lib/i18n/locales/fa-IR/translation.json | 19 ++++++++++ src/lib/i18n/locales/fi-FI/translation.json | 19 ++++++++++ src/lib/i18n/locales/fr-CA/translation.json | 20 +++++++++++ src/lib/i18n/locales/fr-FR/translation.json | 20 +++++++++++ src/lib/i18n/locales/gl-ES/translation.json | 19 ++++++++++ src/lib/i18n/locales/he-IL/translation.json | 20 +++++++++++ src/lib/i18n/locales/hi-IN/translation.json | 19 ++++++++++ src/lib/i18n/locales/hr-HR/translation.json | 20 +++++++++++ src/lib/i18n/locales/hu-HU/translation.json | 19 ++++++++++ src/lib/i18n/locales/id-ID/translation.json | 18 ++++++++++ src/lib/i18n/locales/ie-GA/translation.json | 19 ++++++++++ src/lib/i18n/locales/it-IT/translation.json | 20 +++++++++++ src/lib/i18n/locales/ja-JP/translation.json | 18 ++++++++++ src/lib/i18n/locales/ka-GE/translation.json | 19 ++++++++++ src/lib/i18n/locales/kab-DZ/translation.json | 19 ++++++++++ src/lib/i18n/locales/ko-KR/translation.json | 18 ++++++++++ src/lib/i18n/locales/lt-LT/translation.json | 21 +++++++++++ src/lib/i18n/locales/lv-LV/translation.json | 20 +++++++++++ src/lib/i18n/locales/ms-MY/translation.json | 18 ++++++++++ src/lib/i18n/locales/nb-NO/translation.json | 19 ++++++++++ src/lib/i18n/locales/nl-NL/translation.json | 19 ++++++++++ src/lib/i18n/locales/pa-IN/translation.json | 19 ++++++++++ src/lib/i18n/locales/pl-PL/translation.json | 21 +++++++++++ src/lib/i18n/locales/pt-BR/translation.json | 20 +++++++++++ src/lib/i18n/locales/pt-PT/translation.json | 20 +++++++++++ src/lib/i18n/locales/ro-RO/translation.json | 20 +++++++++++ src/lib/i18n/locales/ru-RU/translation.json | 21 +++++++++++ src/lib/i18n/locales/sk-SK/translation.json | 21 +++++++++++ src/lib/i18n/locales/sr-RS/translation.json | 20 +++++++++++ src/lib/i18n/locales/sv-SE/translation.json | 19 ++++++++++ src/lib/i18n/locales/ta-IN/translation.json | 19 ++++++++++ src/lib/i18n/locales/th-TH/translation.json | 18 ++++++++++ src/lib/i18n/locales/tk-TM/translation.json | 19 ++++++++++ src/lib/i18n/locales/tr-TR/translation.json | 19 ++++++++++ src/lib/i18n/locales/ug-CN/translation.json | 19 ++++++++++ src/lib/i18n/locales/uk-UA/translation.json | 21 +++++++++++ src/lib/i18n/locales/ur-PK/translation.json | 19 ++++++++++ .../i18n/locales/uz-Cyrl-UZ/translation.json | 19 ++++++++++ .../i18n/locales/uz-Latn-Uz/translation.json | 19 ++++++++++ src/lib/i18n/locales/vi-VN/translation.json | 18 ++++++++++ src/lib/i18n/locales/zh-CN/translation.json | 18 ++++++++++ src/lib/i18n/locales/zh-TW/translation.json | 18 ++++++++++ 74 files changed, 1244 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47f6a27199..7d5f34d74f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -214,7 +214,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 📝 **Writing block parsing reliability.** ":::writing" blocks now parse more reliably when headers or extra inline text are present, preventing malformed rendering and duplicate output artifacts. [#23174](https://github.com/open-webui/open-webui/issues/23174) - 🧾 **Code block line break reliability.** Blank lines in submitted code blocks are now preserved more reliably instead of being collapsed. [Commit](https://github.com/open-webui/open-webui/commit/1be9627dd27ffe75957729a4a0d1682a98684f01), [#20302](https://github.com/open-webui/open-webui/issues/20302), [#23451](https://github.com/open-webui/open-webui/pull/23451) - ✂️ **Citation spacing cleanup.** When citations are disabled for a model, citation markers and their leftover spacing are now removed together so punctuation and copied text remain cleanly formatted. [#23141](https://github.com/open-webui/open-webui/issues/23141) -- 🧰 **Pipe tool access.** Pipe functions now receive built-in and MCP tools in __tools__, so tools like Web Search and code execution are available when enabled. [#23365](https://github.com/open-webui/open-webui/issues/23365) +- 🧰 **Pipe tool access.** Pipe functions now receive built-in and MCP tools in **tools**, so tools like Web Search and code execution are available when enabled. [#23365](https://github.com/open-webui/open-webui/issues/23365) - 📚 **Batch file processing database handling.** Batch knowledge file processing now consistently uses the active database session, preventing failures caused by missing database context during file ownership checks and update writes. [#23137](https://github.com/open-webui/open-webui/issues/23137) - ⚙️ **Default model parameter loading.** The "DEFAULT_MODEL_PARAMS" environment variable is now parsed and applied correctly, so default generation settings are honored reliably without being ignored at startup. [#23223](https://github.com/open-webui/open-webui/pull/23223) - 🔧 **Web search settings save reliability.** Saving web search configuration now works without server errors, so administrators can update "WEB_FETCH_MAX_CONTENT_LENGTH" and related retrieval settings successfully from the admin interface. [Commit](https://github.com/open-webui/open-webui/commit/36d02aa1477aa1b4e7fb59d022f99693ebfa8667), [#23127](https://github.com/open-webui/open-webui/issues/23127) diff --git a/backend/open_webui/internal/db.py b/backend/open_webui/internal/db.py index e3b4a110cd..25aa94591b 100644 --- a/backend/open_webui/internal/db.py +++ b/backend/open_webui/internal/db.py @@ -56,10 +56,7 @@ def extract_ssl_mode_from_url(url: str) -> tuple[str, str | None]: Non-PostgreSQL URLs are returned unchanged with ``ssl_mode=None``. """ - if not url or not any( - url.startswith(prefix) - for prefix in ('postgresql://', 'postgresql+', 'postgres://') - ): + if not url or not any(url.startswith(prefix) for prefix in ('postgresql://', 'postgresql+', 'postgres://')): return url, None parsed = urlparse(url) @@ -126,7 +123,6 @@ def reattach_ssl_mode_to_url(url_without_ssl: str, ssl_mode: str | None) -> str: return f'{url_without_ssl}{separator}sslmode={ssl_mode}' - class JSONField(types.TypeDecorator): impl = types.Text cache_ok = True @@ -188,7 +184,9 @@ if ENABLE_DB_MIGRATIONS: DATABASE_URL_WITHOUT_SSL, DATABASE_SSL_MODE = extract_ssl_mode_from_url(DATABASE_URL) # For psycopg2 (sync engine), re-append sslmode=. -SQLALCHEMY_DATABASE_URL = reattach_ssl_mode_to_url(DATABASE_URL_WITHOUT_SSL, DATABASE_SSL_MODE) if DATABASE_SSL_MODE else DATABASE_URL +SQLALCHEMY_DATABASE_URL = ( + reattach_ssl_mode_to_url(DATABASE_URL_WITHOUT_SSL, DATABASE_SSL_MODE) if DATABASE_SSL_MODE else DATABASE_URL +) def _make_async_url(url: str) -> str: @@ -332,15 +330,13 @@ get_db = contextmanager(get_session) # ============================================================ # Use the SSL-stripped URL for asyncpg — SSL is injected via connect_args. -ASYNC_SQLALCHEMY_DATABASE_URL = _make_async_url(DATABASE_URL_WITHOUT_SSL if DATABASE_SSL_MODE else SQLALCHEMY_DATABASE_URL) +ASYNC_SQLALCHEMY_DATABASE_URL = _make_async_url( + DATABASE_URL_WITHOUT_SSL if DATABASE_SSL_MODE else SQLALCHEMY_DATABASE_URL +) if 'sqlite' in ASYNC_SQLALCHEMY_DATABASE_URL: # Generous default — async coroutines + no session sharing = high connection demand. - _sqlite_pool_size = ( - DATABASE_POOL_SIZE - if isinstance(DATABASE_POOL_SIZE, int) and DATABASE_POOL_SIZE > 0 - else 512 - ) + _sqlite_pool_size = DATABASE_POOL_SIZE if isinstance(DATABASE_POOL_SIZE, int) and DATABASE_POOL_SIZE > 0 else 512 async_engine = create_async_engine( ASYNC_SQLALCHEMY_DATABASE_URL, connect_args={'check_same_thread': False}, diff --git a/backend/open_webui/models/calendar.py b/backend/open_webui/models/calendar.py index dbb070013e..47f0a6f722 100644 --- a/backend/open_webui/models/calendar.py +++ b/backend/open_webui/models/calendar.py @@ -307,8 +307,6 @@ class CalendarTable: cal = result.scalars().first() return await self._to_calendar_model(cal, db=db) if cal else None - - async def insert_new_calendar( self, user_id: str, form_data: CalendarForm, db: Optional[AsyncSession] = None ) -> Optional[CalendarModel]: diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index cafb8fe4f0..b9bfcc12c8 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -122,9 +122,7 @@ def build_loader_from_config(request): ) -def _extract_text_from_binary_response( - request, response: requests.Response, url: str -) -> tuple[str, list]: +def _extract_text_from_binary_response(request, response: requests.Response, url: str) -> tuple[str, list]: """Download response body to a temp file and extract text using the Loader pipeline.""" import mimetypes import tempfile diff --git a/backend/open_webui/routers/calendar.py b/backend/open_webui/routers/calendar.py index 152b932234..c95888ebfa 100644 --- a/backend/open_webui/routers/calendar.py +++ b/backend/open_webui/routers/calendar.py @@ -55,9 +55,7 @@ async def _user_has_automations(request: Request, user) -> bool: return False if user.role == 'admin': return True - return await has_permission( - user.id, 'features.automations', request.app.state.config.USER_PERMISSIONS - ) + return await has_permission(user.id, 'features.automations', request.app.state.config.USER_PERMISSIONS) async def _check_calendar_access(calendar_id: str, user: UserModel, permission: str = 'write') -> CalendarModel: diff --git a/backend/open_webui/routers/configs.py b/backend/open_webui/routers/configs.py index 68e1d129dc..02b16d8e5b 100644 --- a/backend/open_webui/routers/configs.py +++ b/backend/open_webui/routers/configs.py @@ -293,7 +293,9 @@ async def verify_terminal_server_connection( ) as session: # Orchestrators expose a policies API; plain terminals don't. try: - async with session.get(f'{base_url}/api/v1/policies', headers=headers, ssl=AIOHTTP_CLIENT_SESSION_SSL) as resp: + async with session.get( + f'{base_url}/api/v1/policies', headers=headers, ssl=AIOHTTP_CLIENT_SESSION_SSL + ) as resp: if resp.ok: return {'status': True, 'type': 'orchestrator'} except Exception: @@ -301,7 +303,9 @@ async def verify_terminal_server_connection( # Fall back to open-terminal config endpoint. try: - async with session.get(f'{base_url}/api/config', headers=headers, ssl=AIOHTTP_CLIENT_SESSION_SSL) as resp: + async with session.get( + f'{base_url}/api/config', headers=headers, ssl=AIOHTTP_CLIENT_SESSION_SSL + ) as resp: if resp.ok: return {'status': True, 'type': 'terminal'} except Exception: @@ -342,7 +346,9 @@ async def put_terminal_server_policy( timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT), ) as session: policy_url = f'{base_url}/api/v1/policies/{form_data.policy_id}' - async with session.put(policy_url, headers=headers, json=form_data.policy_data, ssl=AIOHTTP_CLIENT_SESSION_SSL) as resp: + async with session.put( + policy_url, headers=headers, json=form_data.policy_data, ssl=AIOHTTP_CLIENT_SESSION_SSL + ) as resp: if resp.ok: return await resp.json() detail = await resp.text() @@ -369,7 +375,9 @@ async def verify_tool_servers_config(request: Request, form_data: ToolServerConn trust_env=True, timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT), ) as session: - async with session.get(discovery_url, ssl=AIOHTTP_CLIENT_SESSION_SSL) as oauth_server_metadata_response: + async with session.get( + discovery_url, ssl=AIOHTTP_CLIENT_SESSION_SSL + ) as oauth_server_metadata_response: if oauth_server_metadata_response.status == 200: try: oauth_server_metadata = OAuthMetadata.model_validate( diff --git a/backend/open_webui/routers/functions.py b/backend/open_webui/routers/functions.py index baec1f0870..f40cd1ab82 100644 --- a/backend/open_webui/routers/functions.py +++ b/backend/open_webui/routers/functions.py @@ -117,7 +117,9 @@ async def load_function_from_url(request: Request, form_data: LoadUrlForm, user= async with aiohttp.ClientSession( trust_env=True, timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT) ) as session: - async with session.get(url, headers={'Content-Type': 'application/json'}, ssl=AIOHTTP_CLIENT_SESSION_SSL) as resp: + async with session.get( + url, headers={'Content-Type': 'application/json'}, ssl=AIOHTTP_CLIENT_SESSION_SSL + ) as resp: if resp.status != 200: raise HTTPException(status_code=resp.status, detail='Failed to fetch the function') data = await resp.text() diff --git a/backend/open_webui/routers/tools.py b/backend/open_webui/routers/tools.py index 4c3e77e566..04d845c3de 100644 --- a/backend/open_webui/routers/tools.py +++ b/backend/open_webui/routers/tools.py @@ -274,7 +274,9 @@ async def load_tool_from_url(request: Request, form_data: LoadUrlForm, user=Depe async with aiohttp.ClientSession( trust_env=True, timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT) ) as session: - async with session.get(url, headers={'Content-Type': 'application/json'}, ssl=AIOHTTP_CLIENT_SESSION_SSL) as resp: + async with session.get( + url, headers={'Content-Type': 'application/json'}, ssl=AIOHTTP_CLIENT_SESSION_SSL + ) as resp: if resp.status != 200: raise HTTPException(status_code=resp.status, detail='Failed to fetch the tool') data = await resp.text() diff --git a/backend/open_webui/utils/files.py b/backend/open_webui/utils/files.py index 7d0d9da2c2..8149987fe4 100644 --- a/backend/open_webui/utils/files.py +++ b/backend/open_webui/utils/files.py @@ -34,19 +34,19 @@ MARKDOWN_IMAGE_URL_PATTERN = re.compile(r'!\[(.*?)\]\((.+?)\)', re.IGNORECASE) # Extension-based MIME fallback, only used when ENABLE_IMAGE_CONTENT_TYPE_EXTENSION_FALLBACK is True. _IMAGE_MIME_FALLBACK = { - ".webp": "image/webp", - ".png": "image/png", - ".jpg": "image/jpeg", - ".jpeg": "image/jpeg", - ".gif": "image/gif", - ".svg": "image/svg+xml", - ".bmp": "image/bmp", - ".tiff": "image/tiff", - ".tif": "image/tiff", - ".ico": "image/x-icon", - ".heic": "image/heic", - ".heif": "image/heif", - ".avif": "image/avif", + '.webp': 'image/webp', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.gif': 'image/gif', + '.svg': 'image/svg+xml', + '.bmp': 'image/bmp', + '.tiff': 'image/tiff', + '.tif': 'image/tiff', + '.ico': 'image/x-icon', + '.heic': 'image/heic', + '.heif': 'image/heif', + '.avif': 'image/avif', } @@ -75,10 +75,7 @@ async def get_image_base64_from_url(url: str) -> Optional[str]: if file_path.is_file(): with open(file_path, 'rb') as image_file: encoded_string = base64.b64encode(image_file.read()).decode('utf-8') - content_type = ( - mimetypes.guess_type(file_path.name)[0] - or (file.meta or {}).get('content_type') - ) + content_type = mimetypes.guess_type(file_path.name)[0] or (file.meta or {}).get('content_type') if not content_type and ENABLE_IMAGE_CONTENT_TYPE_EXTENSION_FALLBACK: content_type = _IMAGE_MIME_FALLBACK.get(file_path.suffix.lower()) if not content_type: @@ -204,10 +201,7 @@ async def get_image_base64_from_file_id(id: str) -> Optional[str]: if file_path.is_file(): with open(file_path, 'rb') as image_file: encoded_string = base64.b64encode(image_file.read()).decode('utf-8') - content_type = ( - mimetypes.guess_type(file_path.name)[0] - or (file.meta or {}).get('content_type') - ) + content_type = mimetypes.guess_type(file_path.name)[0] or (file.meta or {}).get('content_type') if not content_type and ENABLE_IMAGE_CONTENT_TYPE_EXTENSION_FALLBACK: content_type = _IMAGE_MIME_FALLBACK.get(file_path.suffix.lower()) if not content_type: diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index 3f4eac7e91..9f3ab0bce4 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -908,7 +908,9 @@ async def get_terminal_cwd( timeout=aiohttp.ClientTimeout(total=5), trust_env=True, ) as session: - async with session.get(cwd_url, headers=headers, cookies=cookies or {}, ssl=AIOHTTP_CLIENT_SESSION_SSL) as resp: + async with session.get( + cwd_url, headers=headers, cookies=cookies or {}, ssl=AIOHTTP_CLIENT_SESSION_SSL + ) as resp: if resp.status == 200: data = await resp.json() return data.get('cwd') @@ -943,7 +945,9 @@ async def get_terminal_system_prompt( return None # 2. Fetch system prompt - async with session.get(f'{base}/system', headers=headers, cookies=cookies or {}, ssl=AIOHTTP_CLIENT_SESSION_SSL) as resp: + async with session.get( + f'{base}/system', headers=headers, cookies=cookies or {}, ssl=AIOHTTP_CLIENT_SESSION_SSL + ) as resp: if resp.status == 200: data = await resp.json() return data.get('prompt') diff --git a/src/lib/components/calendar/CalendarSidebar.svelte b/src/lib/components/calendar/CalendarSidebar.svelte index 76d762760a..d3ea51a472 100644 --- a/src/lib/components/calendar/CalendarSidebar.svelte +++ b/src/lib/components/calendar/CalendarSidebar.svelte @@ -94,7 +94,10 @@ @@ -219,11 +222,7 @@ stroke="currentColor" class="size-3" > - + {/if} diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index fbd91e512c..03af994a68 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -757,10 +757,7 @@ const selectedFolderSubscribe = selectedFolder.subscribe(async (folder) => { await tick(); - if ( - folder?.data?.model_ids && - !equal(selectedModels, folder.data.model_ids) - ) { + if (folder?.data?.model_ids && !equal(selectedModels, folder.data.model_ids)) { selectedModels = folder.data.model_ids; console.log('Set selectedModels from folder data:', selectedModels); @@ -1836,8 +1833,7 @@ ); chatFiles = chatFiles.filter( // Remove duplicates - (item, index, array) => - array.findIndex((i) => equal(i, item)) === index + (item, index, array) => array.findIndex((i) => equal(i, item)) === index ); // Create user message @@ -2176,10 +2172,7 @@ ) ); // Remove duplicates - files = files.filter( - (item, index, array) => - array.findIndex((i) => equal(i, item)) === index - ); + files = files.filter((item, index, array) => array.findIndex((i) => equal(i, item)) === index); scrollToBottom(); eventTarget.dispatchEvent( diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index aeb96af5b0..11cd749987 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -1941,7 +1941,9 @@ {#if !history?.currentId || history.messages[history.currentId]?.done == true} - {@const hasDirectToolServerAccess = $_user?.role === 'admin' || ($_user?.permissions?.features?.direct_tool_servers ?? true)} + {@const hasDirectToolServerAccess = + $_user?.role === 'admin' || + ($_user?.permissions?.features?.direct_tool_servers ?? true)} {#if terminalCapableModels.length > 0 && (($terminalServers ?? []).some((t) => t.id) || (hasDirectToolServerAccess && (($terminalServers ?? []).some((t) => !t.id) || ($settings?.terminalServers ?? []).some((s) => s.url))))} {/if} diff --git a/src/lib/i18n/locales/ar-BH/translation.json b/src/lib/i18n/locales/ar-BH/translation.json index 1b4ff02105..13e9aed4e9 100644 --- a/src/lib/i18n/locales/ar-BH/translation.json +++ b/src/lib/i18n/locales/ar-BH/translation.json @@ -37,8 +37,13 @@ "{{user}}'s Chats": "دردشات {{user}}", "{{webUIName}} Backend Required": "{{webUIName}} مطلوب", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -206,6 +211,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -280,6 +286,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "", "Call feature is not supported when using Web STT engine": "", @@ -417,6 +424,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "اتصالات", @@ -529,6 +537,8 @@ "Delete All Chats": "حذف جميع الدردشات", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "حذف المحادثه.", "Delete chat?": "", "Delete Event": "", @@ -890,6 +900,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "فشل في إنشاء مفتاح API.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1629,6 +1640,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "سجل صوت", "Redirecting you to Open WebUI Community": "OpenWebUI إعادة توجيهك إلى مجتمع ", @@ -1652,6 +1664,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "إزالة", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1902,7 +1915,15 @@ "Start a new conversation": "", "Start of the channel": "بداية القناة", "Start Tag": "", + "Starting in {{count}} minutes_zero": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_two": "", + "Starting in {{count}} minutes_few": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2015,6 +2036,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "شرح شامل", "Thought": "", @@ -2101,6 +2123,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/ar/translation.json b/src/lib/i18n/locales/ar/translation.json index 49a3c5be10..3eb53e68bd 100644 --- a/src/lib/i18n/locales/ar/translation.json +++ b/src/lib/i18n/locales/ar/translation.json @@ -37,8 +37,13 @@ "{{user}}'s Chats": "محادثات المستخدم {{user}}", "{{webUIName}} Backend Required": "يتطلب الخلفية الخاصة بـ {{webUIName}}", "*Prompt node ID(s) are required for image generation": "*معرّف/معرّفات عقدة الموجه مطلوبة لتوليد الصور", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "يتوفر الآن إصدار جديد (v{{LATEST_VERSION}}).", @@ -206,6 +211,7 @@ "Ask a question": "اطرح سؤالاً", "Assistant": "المساعد", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -280,6 +286,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "التقويم", + "Calendar deleted": "", "Calendars": "", "Call": "مكالمة", "Call feature is not supported when using Web STT engine": "ميزة الاتصال غير مدعومة عند استخدام محرك Web STT", @@ -417,6 +424,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "الاتصالات", @@ -529,6 +537,8 @@ "Delete All Chats": "حذف جميع الدردشات", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "حذف المحادثه.", "Delete chat?": "هل تريد حذف المحادثة؟", "Delete Event": "", @@ -890,6 +900,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "فشل في إنشاء مفتاح API.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1629,6 +1640,7 @@ "Reasoning Effort": "جهد الاستدلال", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "سجل صوت", "Redirecting you to Open WebUI Community": "OpenWebUI إعادة توجيهك إلى مجتمع ", @@ -1652,6 +1664,7 @@ "Relevance": "الصلة", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "إزالة", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1902,7 +1915,15 @@ "Start a new conversation": "", "Start of the channel": "بداية القناة", "Start Tag": "", + "Starting in {{count}} minutes_zero": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_two": "", + "Starting in {{count}} minutes_few": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2015,6 +2036,7 @@ "This will delete {{NAME}} and all its contents.": "هذا سيحذف {{NAME}} وكل محتوياته.", "This will delete all models including custom models": "هذا سيحذف جميع النماذج بما في ذلك النماذج المخصصة", "This will delete all models including custom models and cannot be undone.": "هذا سيحذف جميع النماذج بما في ذلك المخصصة ولا يمكن التراجع عن هذا الإجراء.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "هذا سيؤدي إلى إعادة تعيين قاعدة المعرفة ومزامنة جميع الملفات. هل ترغب في المتابعة؟", "Thorough explanation": "شرح شامل", "Thought": "", @@ -2101,6 +2123,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "اكشف الأسرار", "Unpin": "إزالة التثبيت", + "Unpin from Sidebar": "", "Unravel secrets": "فكّ الأسرار", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/az-AZ/translation.json b/src/lib/i18n/locales/az-AZ/translation.json index 9e316d538d..8eec5e5732 100644 --- a/src/lib/i18n/locales/az-AZ/translation.json +++ b/src/lib/i18n/locales/az-AZ/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}} adlı istifadəçinin söhbətləri", "{{webUIName}} Backend Required": "{{webUIName}} üçün Backend tələb olunur", "*Prompt node ID(s) are required for image generation": "*Şəkil yaradılması üçün sorğu (prompt) qovşaq ID-ləri tələb olunur", + "1 hour before": "", "1 Source": "1 Mənbə", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "1 dəq əvvəl", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "İnsanların üzv kimi qoşulduğu əməkdaşlıq kanalı", "A discussion channel where access is controlled by groups and permissions": "Girişin qruplar və icazələrlə idarə olunduğu müzakirə kanalı", "A new version (v{{LATEST_VERSION}}) is now available.": "Yeni versiya (v{{LATEST_VERSION}}) artıq mövcuddur.", @@ -202,6 +207,7 @@ "Ask a question": "Sual verin", "Assistant": "Köməkçi", "Async Embedding Processing": "Asinxron Yerləşdirmə (Embedding) Emalı", + "At time of event": "", "Attach File From Knowledge": "Bilik bazasından fayl əlavə et", "Attach Files": "", "Attach Knowledge": "Bilik əlavə et", @@ -276,6 +282,7 @@ "Bypass Web Loader": "Veb Yükləyicidən Yan Keç", "Cache Base Model List": "Əsas Model Siyahısını Keşlə", "Calendar": "Təqvim", + "Calendar deleted": "", "Calendars": "", "Call": "Zəng", "Call feature is not supported when using Web STT engine": "Veb STT mühərriki istifadə edildikdə zəng funksiyası dəstəklənmir", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Öz OpenAPI uyğun xarici alət serverlərinizə qoşulun.", "Connected ({{type}})": "", "Connection failed": "Bağlantı uğursuz oldu", + "Connection lost. Reconnecting...": "", "Connection successful": "Bağlantı uğurludur", "Connection Type": "Bağlantı növü", "Connections": "Bağlantılar", @@ -525,6 +533,8 @@ "Delete All Chats": "Bütün çatları sil", "Delete all contents inside this folder": "Bu qovluğun daxilindəki bütün məzmunu sil", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Çatı sil", "Delete chat?": "Çat silinsin?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "{{URL}} terminal serverinə qoşulmaq mümkün olmadı", "Failed to copy link": "Link kopyalanmadı", "Failed to create API Key.": "API açarı yaradılmadı.", + "Failed to delete calendar": "", "Failed to delete note": "Qeyd silinmədi", "Failed to download image": "Şəkil yüklənmədi", "Failed to extract content from the file: {{error}}": "Fayldan məzmun çıxarıla bilmədi: {{error}}", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Mühakimə səyi", "Reasoning Tags": "Mühakimə etiketləri", "Recently Used": "", + "Reconnected": "", "Record": "Yaz (səs)", "Record voice": "Səsi yaz", "Redirecting you to Open WebUI Community": "Open WebUI İcmasına yönləndirilirsiniz", @@ -1648,6 +1660,7 @@ "Relevance": "Uyğunluq", "Relevance Threshold": "Uyğunluq həddi", "Remember Dismissal": "İmtinanı yadda saxla", + "Reminder": "", "Remove": "Çıxar", "Remove {{MODELID}} from list.": "{{MODELID}} siyahıdan çıxarılsın.", "Remove action": "Əməliyyatı çıxar", @@ -1894,7 +1907,11 @@ "Start a new conversation": "Yeni söhbətə başlayın", "Start of the channel": "Kanalın başlanğıcı", "Start Tag": "Start Tag", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Starting kernel...", + "Starting now": "", "State": "", "Status": "Status", "Status cleared successfully": "Status uğurla təmizləndi", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Bu, {{NAME}} adlı elementi və onun bütün məzmununu siləcək.", "This will delete all models including custom models": "Bu, fərdi modellər də daxil olmaqla bütün modelləri siləcək", "This will delete all models including custom models and cannot be undone.": "Bu, fərdi modellər də daxil olmaqla bütün modelləri siləcək və geri qaytarıla bilməz.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Bu, bilik bazasını sıfırlayacaq və bütün faylları sinxronizasiya edəcək. Davam etmək istəyirsiniz?", "Thorough explanation": "Ətraflı izahat", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "{{FROM_NOW}} sonra yaddaşdan silinir", "Unlock mysteries": "Sirrləri açın", "Unpin": "Sabitlənmişdən çıxar", + "Unpin from Sidebar": "", "Unravel secrets": "Gizlinləri üzə çıxarın", "Unshare Chat": "Çatı paylaşımı dayandır", "Unsupported file type.": "Dəstəklənməyən fayl növü.", diff --git a/src/lib/i18n/locales/bg-BG/translation.json b/src/lib/i18n/locales/bg-BG/translation.json index 51dbe73be0..685debf883 100644 --- a/src/lib/i18n/locales/bg-BG/translation.json +++ b/src/lib/i18n/locales/bg-BG/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}}'s чатове", "{{webUIName}} Backend Required": "{{webUIName}} Изисква се Бекенд", "*Prompt node ID(s) are required for image generation": "*Идентификатор(ите) на възел-а се изисква(т) за генериране на изображения", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Вече е налична нова версия (v{{LATEST_VERSION}}).", @@ -202,6 +207,7 @@ "Ask a question": "Задайте въпрос", "Assistant": "Асистент", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "Календар", + "Calendar deleted": "", "Calendars": "", "Call": "Обаждане", "Call feature is not supported when using Web STT engine": "Функцията за обаждане не се поддържа при използването на Web STT двигател", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "Връзки", @@ -525,6 +533,8 @@ "Delete All Chats": "Изтриване на всички чатове", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Изтриване на Чат", "Delete chat?": "Изтриване на чата?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Неуспешно създаване на API ключ.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Усилие за разсъждение", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "Запиши", "Record voice": "Записване на глас", "Redirecting you to Open WebUI Community": "Пренасочване към OpenWebUI общността", @@ -1648,6 +1660,7 @@ "Relevance": "Релевантност", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Изтриване", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "Начало на канала", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Това ще изтрие {{NAME}} и цялото му съдържание.", "This will delete all models including custom models": "Това ще изтрие всички модели, включително персонализираните модели", "This will delete all models including custom models and cannot be undone.": "Това ще изтрие всички модели, включително персонализираните модели, и не може да бъде отменено.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Това ще нулира базата знания и ще синхронизира всички файлове. Желаете ли да продължите?", "Thorough explanation": "Подробно обяснение", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "Разкрий мистерии", "Unpin": "Откачи", + "Unpin from Sidebar": "", "Unravel secrets": "Разгадай тайни", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/bn-BD/translation.json b/src/lib/i18n/locales/bn-BD/translation.json index 5a1589d3b4..9437c8a347 100644 --- a/src/lib/i18n/locales/bn-BD/translation.json +++ b/src/lib/i18n/locales/bn-BD/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}}র চ্যাটস", "{{webUIName}} Backend Required": "{{webUIName}} ব্যাকএন্ড আবশ্যক", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -202,6 +207,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "", "Call feature is not supported when using Web STT engine": "", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "কানেকশনগুলো", @@ -525,6 +533,8 @@ "Delete All Chats": "সব চ্যাট মুছে ফেলুন", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "চ্যাট মুছে ফেলুন", "Delete chat?": "", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "API Key তৈরি করা যায়নি।", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "ভয়েস রেকর্ড করুন", "Redirecting you to Open WebUI Community": "আপনাকে OpenWebUI কমিউনিটিতে পাঠানো হচ্ছে", @@ -1648,6 +1660,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "রিমুভ করুন", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "চ্যানেলের শুরু", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "পুঙ্খানুপুঙ্খ ব্যাখ্যা", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/bo-TB/translation.json b/src/lib/i18n/locales/bo-TB/translation.json index c7f3716239..a65771c7b5 100644 --- a/src/lib/i18n/locales/bo-TB/translation.json +++ b/src/lib/i18n/locales/bo-TB/translation.json @@ -32,8 +32,13 @@ "{{user}}'s Chats": "{{user}} ཡི་ཁ་བརྡ།", "{{webUIName}} Backend Required": "{{webUIName}} རྒྱབ་སྣེ་དགོས།", "*Prompt node ID(s) are required for image generation": "*པར་བཟོའི་ཆེད་དུ་འགུལ་སློང་མདུད་ཚེག་གི་ ID(s) དགོས།", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "པར་གཞི་གསར་པ། (v{{LATEST_VERSION}}) ད་ལྟ་ཡོད།", @@ -201,6 +206,7 @@ "Ask a question": "དྲི་བ་ཞིག་འདྲི་བ།", "Assistant": "ལག་རོགས་པ།", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -275,6 +281,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "ལོ་ཐོ།", + "Calendar deleted": "", "Calendars": "", "Call": "སྐད་འབོད།", "Call feature is not supported when using Web STT engine": "Web STT མ་ལག་སྤྱོད་སྐབས་སྐད་འབོད་ཀྱི་ཁྱད་ཆོས་ལ་རྒྱབ་སྐྱོར་མེད།", @@ -412,6 +419,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "ཁྱེད་རང་གི་ OpenAPI དང་མཐུན་པའི་ཕྱི་རོལ་ལག་ཆའི་སར་བར་ལ་སྦྲེལ་བ།", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "སྦྲེལ་མཐུད།", @@ -524,6 +532,8 @@ "Delete All Chats": "ཁ་བརྡ་ཡོངས་རྫོགས་བསུབ་པ།", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "ཁ་བརྡ་བསུབ་པ།", "Delete chat?": "ཁ་བརྡ་བསུབ་པ།?", "Delete Event": "", @@ -885,6 +895,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "API ལྡེ་མིག་བཟོ་མ་ཐུབ།", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1624,6 +1635,7 @@ "Reasoning Effort": "རྒྱུ་མཚན་འདྲེན་པའི་འབད་བརྩོན།", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "སྐད་སྒྲ་ཕབ་པ།", "Redirecting you to Open WebUI Community": "ཁྱེད་ Open WebUI སྤྱི་ཚོགས་ལ་ཁ་ཕྱོགས་སྒྱུར་བཞིན་པ།", @@ -1647,6 +1659,7 @@ "Relevance": "འབྲེལ་ཡོད་རང་བཞིན།", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "འདོར་བ།", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1892,7 +1905,10 @@ "Start a new conversation": "", "Start of the channel": "རྒྱས་ལམ་འགོ་རིམ་", "Start Tag": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2005,6 +2021,7 @@ "This will delete {{NAME}} and all its contents.": "འདིས་ {{NAME}} དང་ དེའི་ནང་དོན་ཡོངས་རྫོགས་ བསུབ་ངེས།", "This will delete all models including custom models": "འདིས་སྲོལ་བཟོས་དཔེ་དབྱིབས་ཚུད་པའི་དཔེ་དབྱིབས་ཡོངས་རྫོགས་བསུབ་ངེས།", "This will delete all models including custom models and cannot be undone.": "འདིས་སྲོལ་བཟོས་དཔེ་དབྱིབས་ཚུད་པའི་དཔེ་དབྱིབས་ཡོངས་རྫོགས་བསུབ་ངེས་པ་དང་ཕྱིར་ལྡོག་བྱེད་མི་ཐུབ།", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "འདིས་ཤེས་བྱའི་རྟེན་གཞི་སླར་སྒྲིག་བྱས་ནས་ཡིག་ཆ་ཡོངས་རྫོགས་མཉམ་སྡེབ་བྱེད་ངེས། ཁྱེད་མུ་མཐུད་འདོད་ཡོད་དམ།", "Thorough explanation": "འགྲེལ་བཤད་ཞིབ་ཚགས།", "Thought": "", @@ -2091,6 +2108,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "གསང་བ་གྲོལ་བ།", "Unpin": "ཕྱིར་འདོན།", + "Unpin from Sidebar": "", "Unravel secrets": "གསང་བ་གྲོལ་བ།", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/bs-BA/translation.json b/src/lib/i18n/locales/bs-BA/translation.json index 3316f8c4a7..d28abefd59 100644 --- a/src/lib/i18n/locales/bs-BA/translation.json +++ b/src/lib/i18n/locales/bs-BA/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "Razgovori korisnika {{user}}", "{{webUIName}} Backend Required": "{{webUIName}} Backend je potreban", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -203,6 +208,7 @@ "Ask a question": "Pitaj pitanje", "Assistant": "Asistent", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "Prikazi znanje", @@ -277,6 +283,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "Poziv", "Call feature is not supported when using Web STT engine": "Značajka poziva nije podržana kada se koristi Web STT mehanizam", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "Konekcija nije uspjela", + "Connection lost. Reconnecting...": "", "Connection successful": "Konekcija uspjesna", "Connection Type": "Tip Konekcije", "Connections": "Povezivanja", @@ -526,6 +534,8 @@ "Delete All Chats": "Izbriši sve razgovore", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Izbriši razgovor", "Delete chat?": "", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Neuspješno stvaranje API ključa.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Snimanje glasa", "Redirecting you to Open WebUI Community": "Preusmjeravanje na OpenWebUI zajednicu", @@ -1649,6 +1661,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Ukloni", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1896,7 +1909,12 @@ "Start a new conversation": "", "Start of the channel": "Početak kanala", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_few": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "Detaljno objašnjenje", "Thought": "", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/ca-ES/translation.json b/src/lib/i18n/locales/ca-ES/translation.json index add558aebf..a3793e5e42 100644 --- a/src/lib/i18n/locales/ca-ES/translation.json +++ b/src/lib/i18n/locales/ca-ES/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "Els xats de {{user}}", "{{webUIName}} Backend Required": "El Backend de {{webUIName}} és necessari", "*Prompt node ID(s) are required for image generation": "*Els identificadors de nodes d'indicacions són necessaris per a la generació d'imatges", + "1 hour before": "", "1 Source": "1 font", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "1m_time_ago", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Un canal de col·laboració on la gent s'uneix com a membres", "A discussion channel where access is controlled by groups and permissions": "Un canal de discussió on l'accés està controlat per grups i permisos", "A new version (v{{LATEST_VERSION}}) is now available.": "Hi ha una nova versió disponible (v{{LATEST_VERSION}}).", @@ -203,6 +208,7 @@ "Ask a question": "Fer una pregunta", "Assistant": "Assistent", "Async Embedding Processing": "Procés d'incrustat asíncron", + "At time of event": "", "Attach File From Knowledge": "Adjuntar arxiu del coneixement", "Attach Files": "Adjuntar arxius", "Attach Knowledge": "Adjuntar coneixement", @@ -277,6 +283,7 @@ "Bypass Web Loader": "Ometre el càrregador web", "Cache Base Model List": "Llista de models base en memòria cau", "Calendar": "Calendari", + "Calendar deleted": "", "Calendars": "", "Call": "Trucada", "Call feature is not supported when using Web STT engine": "La funció de trucada no s'admet quan s'utilitza el motor Web STT", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Connecta als teus propis servidors d'eines externs compatibles amb OpenAPI", "Connected ({{type}})": "Connectat ({{type}})", "Connection failed": "La connexió ha fallat", + "Connection lost. Reconnecting...": "", "Connection successful": "Connexió correcta", "Connection Type": "Tipus de connexió", "Connections": "Connexions", @@ -526,6 +534,8 @@ "Delete All Chats": "Eliminar tots els xats", "Delete all contents inside this folder": "Eliminar tot el contingut d'aquesta carpeta", "Delete automation?": "Eliminar l'automatització", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Eliminar xat", "Delete chat?": "Eliminar el xat?", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "No s'ha pogut connecta al servidor de terminal {{URL}}", "Failed to copy link": "No s'ha pogut copiar l'enllaç", "Failed to create API Key.": "No s'ha pogut crear la clau API.", + "Failed to delete calendar": "", "Failed to delete note": "No s'ha pogut eliminar la nota", "Failed to download image": "No s'ha pogut descarregar la imatge", "Failed to extract content from the file: {{error}}": "No s'ha pogut extreure el contingut del fitxer: {{error}}", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "Esforç de raonament", "Reasoning Tags": "Etiqueta de raonament", "Recently Used": "Recentment utilitzat", + "Reconnected": "", "Record": "Enregistrar", "Record voice": "Enregistrar la veu", "Redirecting you to Open WebUI Community": "Redirigint-te a la comunitat OpenWebUI", @@ -1649,6 +1661,7 @@ "Relevance": "Rellevància", "Relevance Threshold": "Límit de rellevància", "Remember Dismissal": "Recordar la decisió de refutar", + "Reminder": "", "Remove": "Eliminar", "Remove {{MODELID}} from list.": "Eliminar {{MODELID}} de la llista", "Remove action": "Eliminar l'acció", @@ -1896,7 +1909,12 @@ "Start a new conversation": "Iniciar una nova conversa", "Start of the channel": "Inici del canal", "Start Tag": "Etiqueta d'inici", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Iniciant el kernel...", + "Starting now": "", "State": "Estat", "Status": "Estat", "Status cleared successfully": "S'ha eliminat correctament el teu estat", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "Això eliminarà {{NAME}} i tots els continguts.", "This will delete all models including custom models": "Això eliminarà tots els models incloent els personalitzats", "This will delete all models including custom models and cannot be undone.": "Això eliminarà tots els models incloent els personalitzats i no es pot desfer", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Això restablirà la base de coneixement i sincronitzarà tots els fitxers. Vols continuar?", "Thorough explanation": "Explicació en detall", "Thought": "Pensament", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "Es descarrega {{FROM_NOW}}", "Unlock mysteries": "Desbloqueja els misteris", "Unpin": "Alliberar", + "Unpin from Sidebar": "", "Unravel secrets": "Descobreix els secrets", "Unshare Chat": "Deixar de compartir el xat", "Unsupported file type.": "Tipus no suportat", diff --git a/src/lib/i18n/locales/ceb-PH/translation.json b/src/lib/i18n/locales/ceb-PH/translation.json index db49608fee..d1278ac30b 100644 --- a/src/lib/i18n/locales/ceb-PH/translation.json +++ b/src/lib/i18n/locales/ceb-PH/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "", "{{webUIName}} Backend Required": "Backend {{webUIName}} gikinahanglan", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -202,6 +207,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "", "Call feature is not supported when using Web STT engine": "", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "Mga koneksyon", @@ -525,6 +533,8 @@ "Delete All Chats": "", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "", "Delete chat?": "", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Irekord ang tingog", "Redirecting you to Open WebUI Community": "Gi-redirect ka sa komunidad sa OpenWebUI", @@ -1648,6 +1660,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "Sinugdan sa channel", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/cs-CZ/translation.json b/src/lib/i18n/locales/cs-CZ/translation.json index b2ec0ebb05..a787579837 100644 --- a/src/lib/i18n/locales/cs-CZ/translation.json +++ b/src/lib/i18n/locales/cs-CZ/translation.json @@ -35,8 +35,13 @@ "{{user}}'s Chats": "Konverzace uživatele {{user}}", "{{webUIName}} Backend Required": "Je vyžadován backend {{webUIName}}", "*Prompt node ID(s) are required for image generation": "*Pro generování obrázků jsou vyžadována ID uzlů instrukce", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Nová verze (v{{LATEST_VERSION}}) je nyní k dispozici.", @@ -204,6 +209,7 @@ "Ask a question": "Položit otázku", "Assistant": "Asistent", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "Připojit znalosti", @@ -278,6 +284,7 @@ "Bypass Web Loader": "Obejít webový zavaděč", "Cache Base Model List": "Ukládat seznam základních modelů do mezipaměti", "Calendar": "Kalendář", + "Calendar deleted": "", "Calendars": "", "Call": "Volání", "Call feature is not supported when using Web STT engine": "Funkce volání není podporována při použití webového STT jádra.", @@ -415,6 +422,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Připojte se k vlastním externím serverům nástrojů kompatibilním s OpenAPI.", "Connected ({{type}})": "", "Connection failed": "Připojení se nezdařilo", + "Connection lost. Reconnecting...": "", "Connection successful": "Připojení úspěšné", "Connection Type": "Typ připojení", "Connections": "Připojení", @@ -527,6 +535,8 @@ "Delete All Chats": "Smazat všechny konverzace", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Smazat konverzaci", "Delete chat?": "Smazat konverzaci?", "Delete Event": "", @@ -888,6 +898,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "Nepodařilo se zkopírovat odkaz", "Failed to create API Key.": "Nepodařilo se vytvořit API klíč.", + "Failed to delete calendar": "", "Failed to delete note": "Nepodařilo se smazat poznámku", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "Nepodařilo se extrahovat obsah ze souboru: {{error}}", @@ -1627,6 +1638,7 @@ "Reasoning Effort": "reasoning effort", "Reasoning Tags": "reasoning tags", "Recently Used": "", + "Reconnected": "", "Record": "Nahrát", "Record voice": "Nahrát hlas", "Redirecting you to Open WebUI Community": "Přesměrovávám vás do komunity Open WebUI", @@ -1650,6 +1662,7 @@ "Relevance": "Relevance", "Relevance Threshold": "Prahová hodnota relevance", "Remember Dismissal": "Pamatovat si zavření", + "Reminder": "", "Remove": "Odebrat", "Remove {{MODELID}} from list.": "Odebrat {{MODELID}} ze seznamu.", "Remove action": "", @@ -1898,7 +1911,13 @@ "Start a new conversation": "", "Start of the channel": "Začátek kanálu", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_few": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2011,6 +2030,7 @@ "This will delete {{NAME}} and all its contents.": "Tím se smaže {{NAME}} a veškerý jeho obsah.", "This will delete all models including custom models": "Tím se smažou všechny modely včetně vlastních modelů", "This will delete all models including custom models and cannot be undone.": "Tím se smažou všechny modely včetně vlastních a tuto akci nelze vrátit zpět.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Tím se resetuje znalostní báze a synchronizují se všechny soubory. Přejete si pokračovat?", "Thorough explanation": "Důkladné vysvětlení", "Thought": "", @@ -2097,6 +2117,7 @@ "Unloads {{FROM_NOW}}": "Uvolní se {{FROM_NOW}}", "Unlock mysteries": "Odhalte záhady", "Unpin": "Odepnout", + "Unpin from Sidebar": "", "Unravel secrets": "Rozplétejte tajemství", "Unshare Chat": "", "Unsupported file type.": "Nepodporovaný typ souboru.", diff --git a/src/lib/i18n/locales/da-DK/translation.json b/src/lib/i18n/locales/da-DK/translation.json index 09d336d179..cde38de62f 100644 --- a/src/lib/i18n/locales/da-DK/translation.json +++ b/src/lib/i18n/locales/da-DK/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}}s chats", "{{webUIName}} Backend Required": "{{webUIName}} Backend kræves", "*Prompt node ID(s) are required for image generation": "*Prompt node ID(s) er påkrævet for at kunne generere billeder", + "1 hour before": "", "1 Source": "1 kilde", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "En samarbejdskanal hvor folk tilmelder sig som medlemmer", "A discussion channel where access is controlled by groups and permissions": "En diskussionskanal hvor adgang styres af grupper og tilladelser", "A new version (v{{LATEST_VERSION}}) is now available.": "En ny version (v{{LATEST_VERSION}}) er nu tilgængelig.", @@ -202,6 +207,7 @@ "Ask a question": "Stil et spørgsmål", "Assistant": "Assistent", "Async Embedding Processing": "Asynkron embedding processering", + "At time of event": "", "Attach File From Knowledge": "Vedhæft fil fra viden", "Attach Files": "", "Attach Knowledge": "Vedhæft viden", @@ -276,6 +282,7 @@ "Bypass Web Loader": "Omgå Web Loader", "Cache Base Model List": "Cache Base Model List", "Calendar": "Kalender", + "Calendar deleted": "", "Calendars": "", "Call": "Opkald", "Call feature is not supported when using Web STT engine": "Opkaldsfunktion er ikke understøttet for Web STT engine", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Opret forbindelse til dine egne OpenAPI kompatible eksterne værktøjsservere.", "Connected ({{type}})": "", "Connection failed": "Forbindelse mislykkedes", + "Connection lost. Reconnecting...": "", "Connection successful": "Forbindelse lykkedes", "Connection Type": "Forbindelsestype", "Connections": "Forbindelser", @@ -525,6 +533,8 @@ "Delete All Chats": "Slet alle chats", "Delete all contents inside this folder": "Slet alt indhold i denne mappe", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Slet chat", "Delete chat?": "Slet chat?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "Kunne ikke kopiere link", "Failed to create API Key.": "Kunne ikke oprette API-nøgle.", + "Failed to delete calendar": "", "Failed to delete note": "Kunne ikke slette note", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "Kunne ikke udtrække indhold fra filen: {{error}}", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Ræsonnements indsats", "Reasoning Tags": "Ræsonneringstags", "Recently Used": "", + "Reconnected": "", "Record": "Optag", "Record voice": "Optag stemme", "Redirecting you to Open WebUI Community": "Omdirigerer dig til OpenWebUI Community", @@ -1648,6 +1660,7 @@ "Relevance": "Relevans", "Relevance Threshold": "Relevans tærskel", "Remember Dismissal": "Husk afvisning", + "Reminder": "", "Remove": "Fjern", "Remove {{MODELID}} from list.": "Fjern {{MODELID}} fra listen.", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "Start en ny samtale", "Start of the channel": "Kanalens start", "Start Tag": "Start tag", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "Status", "Status cleared successfully": "Status slettet", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Dette vil slette {{NAME}} og alt dens indhold.", "This will delete all models including custom models": "Dette vil slette alle modeller, inklusive brugerdefinerede modeller", "This will delete all models including custom models and cannot be undone.": "Dette vil slette alle modeller, inklusive brugerdefinerede modeller og kan ikke fortrydes.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Dette vil nulstille vidensbasen og synkronisere alle filer. Vil du fortsætte?", "Thorough explanation": "Grundig forklaring", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "Aflaster {{FROM_NOW}}", "Unlock mysteries": "Lås op for mysterier", "Unpin": "Frigør", + "Unpin from Sidebar": "", "Unravel secrets": "Afslør hemmeligheder", "Unshare Chat": "", "Unsupported file type.": "Ikke-understøttet filtype.", diff --git a/src/lib/i18n/locales/de-DE/translation.json b/src/lib/i18n/locales/de-DE/translation.json index 5cd8fc30e6..aec1910274 100644 --- a/src/lib/i18n/locales/de-DE/translation.json +++ b/src/lib/i18n/locales/de-DE/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "Chats von {{user}}", "{{webUIName}} Backend Required": "{{webUIName}}-Backend erforderlich", "*Prompt node ID(s) are required for image generation": "*Prompt-Node-ID(s) sind für die Bildgenerierung erforderlich", + "1 hour before": "", "1 Source": "1 Quelle", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "vor 1 Minute", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Ein Kanal zur Zusammenarbeit, dem Mitglieder beitreten können", "A discussion channel where access is controlled by groups and permissions": "Ein Diskussionskanal, dessen Zugriff durch Gruppen und Berechtigungen gesteuert wird", "A new version (v{{LATEST_VERSION}}) is now available.": "Eine neue Version (v{{LATEST_VERSION}}) ist jetzt verfügbar.", @@ -202,6 +207,7 @@ "Ask a question": "Stellen Sie eine Frage", "Assistant": "Assistent", "Async Embedding Processing": "Asynchrone Embedding-Verarbeitung", + "At time of event": "", "Attach File From Knowledge": "Datei aus Wissensspeicher anhängen", "Attach Files": "Dateien anhängen", "Attach Knowledge": "Wissen anhängen", @@ -276,6 +282,7 @@ "Bypass Web Loader": "Web-Loader umgehen", "Cache Base Model List": "Basismodell-Liste cachen", "Calendar": "Kalender", + "Calendar deleted": "", "Calendars": "", "Call": "Anruf", "Call feature is not supported when using Web STT engine": "Die Anruffunktion wird bei Verwendung der Web-STT-Engine nicht unterstützt.", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Verbinden Sie Ihre eigenen OpenAPI-kompatiblen externen Tool-Server.", "Connected ({{type}})": "Verbunden ({{type}})", "Connection failed": "Verbindung fehlgeschlagen", + "Connection lost. Reconnecting...": "", "Connection successful": "Verbindung erfolgreich", "Connection Type": "Verbindungstyp", "Connections": "Verbindungen", @@ -525,6 +533,8 @@ "Delete All Chats": "Alle Chats löschen", "Delete all contents inside this folder": "Alle Inhalte in diesem Ordner löschen", "Delete automation?": "Automatisierung löschen?", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Chat löschen", "Delete chat?": "Chat löschen?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "Fehler beim Verbinden zum Terminal Server {{URL}}", "Failed to copy link": "Link konnte nicht kopiert werden", "Failed to create API Key.": "API-Schlüssel konnte nicht erstellt werden.", + "Failed to delete calendar": "", "Failed to delete note": "Notiz konnte nicht gelöscht werden", "Failed to download image": "Bild konnte nicht heruntergeladen werden", "Failed to extract content from the file: {{error}}": "Inhaltsextraktion fehlgeschlagen: {{error}}", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Reasoning Effort", "Reasoning Tags": "Reasoning Tags", "Recently Used": "Kürzlich verwendet", + "Reconnected": "", "Record": "Aufnehmen", "Record voice": "Stimme aufnehmen", "Redirecting you to Open WebUI Community": "Sie werden zur Open WebUI Community weitergeleitet", @@ -1648,6 +1660,7 @@ "Relevance": "Relevanz", "Relevance Threshold": "Relevanzschwelle", "Remember Dismissal": "Ausblendung merken", + "Reminder": "", "Remove": "Entfernen", "Remove {{MODELID}} from list.": "{{MODELID}} von der Liste entfernen.", "Remove action": "Action entfernen", @@ -1894,7 +1907,11 @@ "Start a new conversation": "Neue Unterhaltung beginnen", "Start of the channel": "Beginn des Kanals", "Start Tag": "Start-Tag", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Kernel starten...", + "Starting now": "", "State": "Zustand", "Status": "Status", "Status cleared successfully": "Status erfolgreich gelöscht", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Dies löscht {{NAME}} und alle Inhalte.", "This will delete all models including custom models": "Dies löscht alle Modelle, einschließlich benutzerdefinierter Modelle", "This will delete all models including custom models and cannot be undone.": "Dies löscht alle Modelle, einschließlich benutzerdefinierter Modelle, und kann nicht rückgängig gemacht werden.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Dadurch wird der Wissensspeicher zurückgesetzt und alle Dateien werden synchronisiert. Möchten Sie fortfahren?", "Thorough explanation": "Ausführliche Erklärung", "Thought": "Gedanke", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "Entlädt {{FROM_NOW}}", "Unlock mysteries": "Geheimnisse entschlüsseln", "Unpin": "Lösen", + "Unpin from Sidebar": "", "Unravel secrets": "Geheimnisse lüften", "Unshare Chat": "Chat-Freigabe entfernen", "Unsupported file type.": "Nicht unterstützter Dateityp.", diff --git a/src/lib/i18n/locales/dg-DG/translation.json b/src/lib/i18n/locales/dg-DG/translation.json index f1e6fddc73..b4a402abac 100644 --- a/src/lib/i18n/locales/dg-DG/translation.json +++ b/src/lib/i18n/locales/dg-DG/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "", "{{webUIName}} Backend Required": "{{webUIName}} Backend Much Required", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -202,6 +207,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "", "Call feature is not supported when using Web STT engine": "", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "Connections", @@ -525,6 +533,8 @@ "Delete All Chats": "", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "", "Delete chat?": "", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Record Bark", "Redirecting you to Open WebUI Community": "Redirecting you to Open WebUI Community", @@ -1648,6 +1660,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "Start of channel", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/el-GR/translation.json b/src/lib/i18n/locales/el-GR/translation.json index 3d59704cdb..22391542aa 100644 --- a/src/lib/i18n/locales/el-GR/translation.json +++ b/src/lib/i18n/locales/el-GR/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "Συνομιλίες του {{user}}", "{{webUIName}} Backend Required": "{{webUIName}} Απαιτείται Backend", "*Prompt node ID(s) are required for image generation": "*Τα αναγνωριστικά κόμβου Prompt απαιτούνται για τη δημιουργία εικόνων", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Μια νέα έκδοση (v{{LATEST_VERSION}}) είναι τώρα διαθέσιμη.", @@ -202,6 +207,7 @@ "Ask a question": "Ρωτήστε μια ερώτηση", "Assistant": "Βοηθός", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "Προσθήκη Knowledge", @@ -276,6 +282,7 @@ "Bypass Web Loader": "Παράκαμψη Φορτωτή Διαδικτύου", "Cache Base Model List": "Αποθήκευση Λίστας Βασικών Μοντέλων Στην Κρυφή Μνήμη", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "Κλήση", "Call feature is not supported when using Web STT engine": "Η λειτουργία κλήσης δεν υποστηρίζεται όταν χρησιμοποιείται η μηχανή Web STT", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Συνδεθείτε στους δικούς σας διακομιστές εξωτερικών εργαλείων συμβατών με OpenAPI.", "Connected ({{type}})": "", "Connection failed": "Σύνδεση απέτυχε", + "Connection lost. Reconnecting...": "", "Connection successful": "Σύνδεση επιτυχής", "Connection Type": "Είδος Σύνδεσης", "Connections": "Συνδέσεις", @@ -525,6 +533,8 @@ "Delete All Chats": "Διαγραφή Όλων των Συνομιλιών", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Διαγραφή Συνομιλίας", "Delete chat?": "Διαγραφή συνομιλίας;", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "Αποτυχία αντιγραφής συνδέσμου", "Failed to create API Key.": "Αποτυχία δημιουργίας Κλειδιού API.", + "Failed to delete calendar": "", "Failed to delete note": "Αποτυχία διαγραφής σημειώσεως", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Εγγραφή φωνής", "Redirecting you to Open WebUI Community": "Μετακατεύθυνση στην Κοινότητα OpenWebUI", @@ -1648,6 +1660,7 @@ "Relevance": "Σχετικότητα", "Relevance Threshold": "Όριο Σχετικότητας", "Remember Dismissal": "Θύμηση Απόρριψης", + "Reminder": "", "Remove": "Αφαίρεση", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "Αρχή του καναλιού", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Αυτό θα διαγράψει το {{NAME}} και όλο το περιεχόμενό του.", "This will delete all models including custom models": "Αυτό θα διαγράψει όλα τα μοντέλα, συμπεριλαμβανομένων των προσαρμοσμένων μοντέλων", "This will delete all models including custom models and cannot be undone.": "Αυτό θα διαγράψει όλα τα μοντέλα, συμπεριλαμβανομένων των προσαρμοσμένων μοντέλων και δεν μπορεί να αναιρεθεί.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Αυτό θα επαναφέρει τη βάση γνώσης και θα συγχρονίσει όλα τα αρχεία. Θέλετε να συνεχίσετε;", "Thorough explanation": "Λεπτομερής εξήγηση", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "Ξεκλείδωμα μυστηρίων", "Unpin": "Ξεκαρφίτσωμα", + "Unpin from Sidebar": "", "Unravel secrets": "Ξετυλίξτε μυστικά", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/en-GB/translation.json b/src/lib/i18n/locales/en-GB/translation.json index d24b7aeda5..88cfb9a311 100644 --- a/src/lib/i18n/locales/en-GB/translation.json +++ b/src/lib/i18n/locales/en-GB/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "", "{{webUIName}} Backend Required": "", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -202,6 +207,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "", "Call feature is not supported when using Web STT engine": "", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "", @@ -525,6 +533,8 @@ "Delete All Chats": "", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "", "Delete chat?": "", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "", "Redirecting you to Open WebUI Community": "", @@ -1648,6 +1660,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/en-US/translation.json b/src/lib/i18n/locales/en-US/translation.json index b53f2ae485..ad0f42f733 100644 --- a/src/lib/i18n/locales/en-US/translation.json +++ b/src/lib/i18n/locales/en-US/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "", "{{webUIName}} Backend Required": "", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -202,6 +207,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "", "Call feature is not supported when using Web STT engine": "", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "", @@ -525,6 +533,8 @@ "Delete All Chats": "", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "", "Delete chat?": "", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "", "Redirecting you to Open WebUI Community": "", @@ -1648,6 +1660,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/es-ES/translation.json b/src/lib/i18n/locales/es-ES/translation.json index 2958a4ddfd..2f44afcee4 100644 --- a/src/lib/i18n/locales/es-ES/translation.json +++ b/src/lib/i18n/locales/es-ES/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "Chats de {{user}}", "{{webUIName}} Backend Required": "{{webUIName}} Servidor Requerido", "*Prompt node ID(s) are required for image generation": "Los ID de nodo son requeridos para la generación de imágenes", + "1 hour before": "", "1 Source": "1 Fuente", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "hace_1m", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Canal colaborativo donde la gente se une como miembro", "A discussion channel where access is controlled by groups and permissions": "Un canal de discusión con el acceso controlado mediante grupos y permisos", "A new version (v{{LATEST_VERSION}}) is now available.": "Nueva versión (v{{LATEST_VERSION}}) disponible.", @@ -203,6 +208,7 @@ "Ask a question": "Haz una pregunta", "Assistant": "Asistente", "Async Embedding Processing": "Procesado Asíncrono al Incrustrar", + "At time of event": "", "Attach File From Knowledge": "Adjuntar Archivo desde Conocimiento", "Attach Files": "Adjuntar Archivos", "Attach Knowledge": "Adjuntar Conocimiento", @@ -277,6 +283,7 @@ "Bypass Web Loader": "Desactivar Cargar de Web", "Cache Base Model List": "Cachear Lista de Cache Modelos", "Calendar": "Calendario", + "Calendar deleted": "", "Calendars": "", "Call": "Llamada", "Call feature is not supported when using Web STT engine": "La funcionalidad de Llamada no está soportada cuando se usa el motor Web STT", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Conectar a tus propios endpoints externos de herramientas compatibles con OpenAPI.", "Connected ({{type}})": "Connectado ({{type}})", "Connection failed": "Conexión fallida", + "Connection lost. Reconnecting...": "", "Connection successful": "Conexión realizada", "Connection Type": "Tipo de Conexión", "Connections": "Conexiones", @@ -526,6 +534,8 @@ "Delete All Chats": "Borrar todos los chats", "Delete all contents inside this folder": "Borrar todo el contenido de esta carpeta", "Delete automation?": "¿Borrar automatización?", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Borrar Chat", "Delete chat?": "¿Borrar el chat?", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "Fallo al conectar al servidor de terminal: {{URL}}", "Failed to copy link": "Fallo al copiar enlace", "Failed to create API Key.": "Fallo al crear la Clave API.", + "Failed to delete calendar": "", "Failed to delete note": "Fallo al eliminar nota", "Failed to download image": "Fallo al descargar imagen", "Failed to extract content from the file: {{error}}": "Fallo al extraer el contenido del archivo: {{error}}", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "Esfuerzo del Razonamiento", "Reasoning Tags": "Etiquetas de Razonamiento", "Recently Used": "Usado Recientemente", + "Reconnected": "", "Record": "Grabar", "Record voice": "Grabar voz", "Redirecting you to Open WebUI Community": "Redireccionando a la Comunidad Open-WebUI", @@ -1649,6 +1661,7 @@ "Relevance": "Relevancia", "Relevance Threshold": "Umbral de Relevancia", "Remember Dismissal": "Recordar Descartes (de notificaciones)", + "Reminder": "", "Remove": "Eliminar", "Remove {{MODELID}} from list.": "Eliminar {{MODELID}} de la lista.", "Remove action": "Eliminar acción", @@ -1896,7 +1909,12 @@ "Start a new conversation": "Comenzar una conversación nueva", "Start of the channel": "Inicio del canal", "Start Tag": "Etiqueta de Inicio", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Iniciando el núcleo...", + "Starting now": "", "State": "Estado", "Status": "Estado", "Status cleared successfully": "Estado limpiado correctamente", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "Esto eliminará {{NAME}} y todo su contenido.", "This will delete all models including custom models": "Esto eliminará todos los modelos, incluidos los modelos personalizados", "This will delete all models including custom models and cannot be undone.": "Esto eliminará todos los modelos, incluidos los modelos personalizados y no se puede deshacer.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Esto reinicializará la base de conocimientos y sincronizará todos los archivos. ¿Desea continuar?", "Thorough explanation": "Explicación exhaustiva", "Thought": "Pensando", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "Descargas {{FROM_NOW}}", "Unlock mysteries": "Desbloquear misterios", "Unpin": "Desfijar", + "Unpin from Sidebar": "", "Unravel secrets": "Desentrañar secretos", "Unshare Chat": "Descompartir Chat", "Unsupported file type.": "Tipo de archivo no soportado", diff --git a/src/lib/i18n/locales/et-EE/translation.json b/src/lib/i18n/locales/et-EE/translation.json index a7da9119d8..a0ce487ea6 100644 --- a/src/lib/i18n/locales/et-EE/translation.json +++ b/src/lib/i18n/locales/et-EE/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}} vestlused", "{{webUIName}} Backend Required": "{{webUIName}} taustaserver on vajalik", "*Prompt node ID(s) are required for image generation": "*Sisendi sõlme ID(d) on piltide genereerimiseks vajalikud", + "1 hour before": "", "1 Source": "1 allikas", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "1m tagasi", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Koostöökanal, kuhu inimesed liituvad liikmetena", "A discussion channel where access is controlled by groups and permissions": "Arutelukanal, kus juurdepääsu kontrollivad grupid ja õigused", "A new version (v{{LATEST_VERSION}}) is now available.": "Uus versioon (v{{LATEST_VERSION}}) on saadaval.", @@ -202,6 +207,7 @@ "Ask a question": "Esita küsimus", "Assistant": "Assistent", "Async Embedding Processing": "Asünkroonne manustamise töötlemine", + "At time of event": "", "Attach File From Knowledge": "Lisa fail teadmistest", "Attach Files": "", "Attach Knowledge": "Lisa teadmised", @@ -276,6 +282,7 @@ "Bypass Web Loader": "Jäta veebilaadija vahele", "Cache Base Model List": "Puhverda baasmudelite nimekiri", "Calendar": "Kalender", + "Calendar deleted": "", "Calendars": "", "Call": "Kõne", "Call feature is not supported when using Web STT engine": "Kõnefunktsioon ei ole Web STT mootorit kasutades toetatud", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Ühendu oma OpenAPI-ga ühilduvate väliste tööriistaserveritega.", "Connected ({{type}})": "", "Connection failed": "Ühendus ebaõnnestus", + "Connection lost. Reconnecting...": "", "Connection successful": "Ühendus õnnestus", "Connection Type": "Ühenduse tüüp", "Connections": "Ühendused", @@ -525,6 +533,8 @@ "Delete All Chats": "Kustuta kõik vestlused", "Delete all contents inside this folder": "Kustuta kogu selle kausta sisu", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Kustuta vestlus", "Delete chat?": "Kustutada vestlus?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "Ühendamine {{URL}} terminali serveriga ebaõnnestus", "Failed to copy link": "Lingi kopeerimine ebaõnnestus", "Failed to create API Key.": "API võtme loomine ebaõnnestus.", + "Failed to delete calendar": "", "Failed to delete note": "Märkme kustutamine ebaõnnestus", "Failed to download image": "Pildi allalaadimine ebaõnnestus", "Failed to extract content from the file: {{error}}": "Failist sisu eraldamine ebaõnnestus: {{error}}", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Arutluspingutus", "Reasoning Tags": "Arutlussildid", "Recently Used": "", + "Reconnected": "", "Record": "Salvesta", "Record voice": "Salvesta hääl", "Redirecting you to Open WebUI Community": "Suunamine Open WebUI kogukonda", @@ -1648,6 +1660,7 @@ "Relevance": "Asjakohasus", "Relevance Threshold": "Asjakohasuse lävi", "Remember Dismissal": "Pea sulgemist meeles", + "Reminder": "", "Remove": "Eemalda", "Remove {{MODELID}} from list.": "Eemalda {{MODELID}} nimekirjast.", "Remove action": "Eemalda toiming", @@ -1894,7 +1907,11 @@ "Start a new conversation": "Alusta uut vestlust", "Start of the channel": "Kanali algus", "Start Tag": "Algussilt", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Kerneli käivitamine...", + "Starting now": "", "State": "", "Status": "Olek", "Status cleared successfully": "Olek edukalt tühjendatud", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "See kustutab {{NAME}} ja kogu selle sisu.", "This will delete all models including custom models": "See kustutab kõik mudelid, sealhulgas kohandatud mudelid", "This will delete all models including custom models and cannot be undone.": "See kustutab kõik mudelid, sealhulgas kohandatud mudelid, ja seda ei saa tagasi võtta.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "See lähtestab teadmiste baasi ja sünkroniseerib kõik failid. Kas soovite jätkata?", "Thorough explanation": "Põhjalik selgitus", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "Laaditakse maha {{FROM_NOW}}", "Unlock mysteries": "Ava mõistatused", "Unpin": "Eemalda kinnitus", + "Unpin from Sidebar": "", "Unravel secrets": "Ava saladused", "Unshare Chat": "Lõpeta vestluse jagamine", "Unsupported file type.": "Toetamata failitüüp.", diff --git a/src/lib/i18n/locales/eu-ES/translation.json b/src/lib/i18n/locales/eu-ES/translation.json index b8314d577a..bbb86b2023 100644 --- a/src/lib/i18n/locales/eu-ES/translation.json +++ b/src/lib/i18n/locales/eu-ES/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}}-ren Txatak", "{{webUIName}} Backend Required": "{{webUIName}} Backend-a Beharrezkoa", "*Prompt node ID(s) are required for image generation": "Prompt nodoaren IDa(k) beharrezkoak dira irudiak sortzeko", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Bertsio berri bat (v{{LATEST_VERSION}}) eskuragarri dago orain.", @@ -202,6 +207,7 @@ "Ask a question": "Egin galdera bat", "Assistant": "Laguntzailea", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "Deia", "Call feature is not supported when using Web STT engine": "Dei funtzioa ez da onartzen Web STT motorra erabiltzean", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "Konexioak", @@ -525,6 +533,8 @@ "Delete All Chats": "Ezabatu Txat Guztiak", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Ezabatu Txata", "Delete chat?": "Ezabatu txata?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Huts egin du API Gakoa sortzean.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Grabatu ahotsa", "Redirecting you to Open WebUI Community": "OpenWebUI Komunitatera berbideratzen", @@ -1648,6 +1660,7 @@ "Relevance": "Garrantzia", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Kendu", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "Kanalaren hasiera", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Honek {{NAME}} eta bere eduki guztiak ezabatuko ditu.", "This will delete all models including custom models": "Honek modelo guztiak ezabatuko ditu, modelo pertsonalizatuak barne", "This will delete all models including custom models and cannot be undone.": "Honek modelo guztiak ezabatuko ditu, modelo pertsonalizatuak barne, eta ezin da desegin.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Honek ezagutza-basea berrezarri eta fitxategi guztiak sinkronizatuko ditu. Jarraitu nahi duzu?", "Thorough explanation": "Azalpen sakona", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "Askatu misterioak", "Unpin": "Kendu aingura", + "Unpin from Sidebar": "", "Unravel secrets": "Askatu sekretuak", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/fa-IR/translation.json b/src/lib/i18n/locales/fa-IR/translation.json index 3dd9dc4d32..39de7bb016 100644 --- a/src/lib/i18n/locales/fa-IR/translation.json +++ b/src/lib/i18n/locales/fa-IR/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}} گفتگوهای", "{{webUIName}} Backend Required": "بکند {{webUIName}} نیاز است.", "*Prompt node ID(s) are required for image generation": "*شناسه(های) گره پرامپت برای تولید تصویر مورد نیاز است", + "1 hour before": "", "1 Source": "۱ منبع", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "نسخه جدید (v{{LATEST_VERSION}}) در دسترس است.", @@ -202,6 +207,7 @@ "Ask a question": "سوالی بپرسید", "Assistant": "دستیار", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "پیوست فایل از دانش", "Attach Files": "", "Attach Knowledge": "پیوست دانش", @@ -276,6 +282,7 @@ "Bypass Web Loader": "دور زدن بارگذاری وب", "Cache Base Model List": "کش لیست مدل پایه", "Calendar": "تقویم", + "Calendar deleted": "", "Calendars": "", "Call": "تماس", "Call feature is not supported when using Web STT engine": "ویژگی تماس هنگام استفاده از موتور Web STT پشتیبانی نمی\u200cشود", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "به سرورهای ابزار خارجی سازگار با OpenAPI خود متصل شوید.", "Connected ({{type}})": "", "Connection failed": "اتصال ناموفق بود", + "Connection lost. Reconnecting...": "", "Connection successful": "اتصال موفقیت\u200cآمیز بود", "Connection Type": "نوع اتصال", "Connections": "ارتباطات", @@ -525,6 +533,8 @@ "Delete All Chats": "حذف همه گفتگوها", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "حذف گپ", "Delete chat?": "گفتگو حذف شود؟", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "کپی لینک ناموفق بود", "Failed to create API Key.": "ایجاد کلید API با خطا مواجه شد.", + "Failed to delete calendar": "", "Failed to delete note": "حذف یادداشت ناموفق بود", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "استخراج محتوا از فایل ناموفق بود: {{error}}", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "تلاش استدلال", "Reasoning Tags": "تگ\u200cهای استدلال", "Recently Used": "", + "Reconnected": "", "Record": "ضبط", "Record voice": "ضبط صدا", "Redirecting you to Open WebUI Community": "در حال هدایت به OpenWebUI Community", @@ -1648,6 +1660,7 @@ "Relevance": "ارتباط", "Relevance Threshold": "آستانه ارتباط", "Remember Dismissal": "به خاطر سپردن رد کردن", + "Reminder": "", "Remove": "حذف", "Remove {{MODELID}} from list.": "حذف {{MODELID}} از لیست.", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "شروع یک مکالمه جدید", "Start of the channel": "آغاز کانال", "Start Tag": "تگ شروع", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "این {{NAME}} و تمام محتویات آن را حذف خواهد کرد.", "This will delete all models including custom models": "این همه مدل\u200cها از جمله مدل\u200cهای سفارشی را حذف خواهد کرد", "This will delete all models including custom models and cannot be undone.": "این همه مدل\u200cها از جمله مدل\u200cهای سفارشی را حذف خواهد کرد و قابل بازگشت نیست.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "این پایگاه دانش را بازنشانی کرده و همه فایل\u200cها را همگام\u200cسازی خواهد کرد. آیا می\u200cخواهید ادامه دهید؟", "Thorough explanation": "توضیح کامل", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "خارج می\u200cشود {{FROM_NOW}}", "Unlock mysteries": "رمزگشایی از اسرار", "Unpin": "برداشتن پین", + "Unpin from Sidebar": "", "Unravel secrets": "کشف رازها", "Unshare Chat": "", "Unsupported file type.": "نوع فایل پشتیبانی نمی\u200cشود.", diff --git a/src/lib/i18n/locales/fi-FI/translation.json b/src/lib/i18n/locales/fi-FI/translation.json index 7c856082bc..16501646eb 100644 --- a/src/lib/i18n/locales/fi-FI/translation.json +++ b/src/lib/i18n/locales/fi-FI/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}}:n keskustelut", "{{webUIName}} Backend Required": "{{webUIName}}-backend vaaditaan", "*Prompt node ID(s) are required for image generation": "Kuvan luomiseen vaaditaan kehote-solmun ID(t)", + "1 hour before": "", "1 Source": "1 lähde", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Yhteistyökanava, johon ihmiset liittyvät jäseninä", "A discussion channel where access is controlled by groups and permissions": "Keskustelukanava, johon pääsyä rajoitetaan ryhmillä ja käyttöoikeuksilla", "A new version (v{{LATEST_VERSION}}) is now available.": "Uusi versio (v{{LATEST_VERSION}}) on nyt saatavilla.", @@ -202,6 +207,7 @@ "Ask a question": "Kysy kysymys", "Assistant": "Avustaja", "Async Embedding Processing": "Asynkroninen upotus prosessointi", + "At time of event": "", "Attach File From Knowledge": "Liitä tiedosto tietämyksestä", "Attach Files": "", "Attach Knowledge": "Liitä tietoa", @@ -276,6 +282,7 @@ "Bypass Web Loader": "Ohita verkkolataaja", "Cache Base Model List": "Malli luettelon välimuisti", "Calendar": "Kalenteri", + "Calendar deleted": "", "Calendars": "", "Call": "Puhelu", "Call feature is not supported when using Web STT engine": "Puhelutoimintoa ei tueta käytettäessä web-puheentunnistusmoottoria", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Yhdistä omat ulkopuoliset OpenAPI yhteensopivat työkalu palvelimet.", "Connected ({{type}})": "Yhdistetty ({{type}})", "Connection failed": "Yhteys epäonnistui", + "Connection lost. Reconnecting...": "", "Connection successful": "Yhteys onnistui", "Connection Type": "Yhteystyyppi", "Connections": "Yhteydet", @@ -525,6 +533,8 @@ "Delete All Chats": "Poista kaikki keskustelut", "Delete all contents inside this folder": "Poista kaikki sisällöt tästä kansiosta", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Poista keskustelu", "Delete chat?": "Haluatko varmasti poistaa tämän keskustelun?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "Yhdistäminen {{URL}} päätepalvelimeen epäonnistui", "Failed to copy link": "Linkin kopiointi epäonnistui", "Failed to create API Key.": "API-avaimen luonti epäonnistui.", + "Failed to delete calendar": "", "Failed to delete note": "Muistiinpanon poistaminen epäonnistui", "Failed to download image": "Kuvan lataaminen epäonnistui", "Failed to extract content from the file: {{error}}": "Tiedoston sisällön pomiminen epäonnistui: {{error}}", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Päättelyn määrä", "Reasoning Tags": "Päättely tagit", "Recently Used": "", + "Reconnected": "", "Record": "Nauhoita", "Record voice": "Nauhoita ääntä", "Redirecting you to Open WebUI Community": "Ohjataan sinut OpenWebUI-yhteisöön", @@ -1648,6 +1660,7 @@ "Relevance": "Relevanssi", "Relevance Threshold": "Relevanssikynnys", "Remember Dismissal": "Muista sulkeminen", + "Reminder": "", "Remove": "Poista", "Remove {{MODELID}} from list.": "Poista {{MODELID}} listalta", "Remove action": "Poista toiminto", @@ -1894,7 +1907,11 @@ "Start a new conversation": "Aloita uusi keskustelu", "Start of the channel": "Kanavan alku", "Start Tag": "Aloitus tagi", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Käynnistetään kerneliä...", + "Starting now": "", "State": "", "Status": "Tila", "Status cleared successfully": "Tila poistettu onnistuneesti", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Tämä poistaa {{NAME}} ja kaikki sen sisällöt.", "This will delete all models including custom models": "Tämä poistaa kaikki mallit mukaan lukien mukautetut mallit", "This will delete all models including custom models and cannot be undone.": "Tämä poistaa kaikki mallit, mukaan lukien mukautetut mallit, eikä sitä voi peruuttaa.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Tämä nollaa tietokannan ja synkronoi kaikki tiedostot. Haluatko jatkaa?", "Thorough explanation": "Perusteellinen selitys", "Thought": "Ajatus", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "Purkuja {{FROM_NOW}}", "Unlock mysteries": "Selvitä arvoituksia", "Unpin": "Irrota kiinnitys", + "Unpin from Sidebar": "", "Unravel secrets": "Avaa salaisuuksia", "Unshare Chat": "Lopeta keskustelun jakaminen", "Unsupported file type.": "Ei tuettu tiedostotyyppi", diff --git a/src/lib/i18n/locales/fr-CA/translation.json b/src/lib/i18n/locales/fr-CA/translation.json index a91ad0b618..d59ea0abf3 100644 --- a/src/lib/i18n/locales/fr-CA/translation.json +++ b/src/lib/i18n/locales/fr-CA/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "Conversations de {{user}}", "{{webUIName}} Backend Required": "Backend {{webUIName}} requis", "*Prompt node ID(s) are required for image generation": "*Les ID de noeud du prompt sont nécessaires pour la génération d'images", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Une nouvelle version (v{{LATEST_VERSION}}) est disponible.", @@ -203,6 +208,7 @@ "Ask a question": "Posez votre question", "Assistant": "Assistant", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -277,6 +283,7 @@ "Bypass Web Loader": "Ignorer le chargeur Web", "Cache Base Model List": "Mettre en cache la liste des modèles de base", "Calendar": "Calendrier", + "Calendar deleted": "", "Calendars": "", "Call": "Appeler", "Call feature is not supported when using Web STT engine": "La fonction d'appel n'est pas prise en charge lors de l'utilisation du moteur Web STT", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Connectez-vous à vos serveurs d'outils externes.", "Connected ({{type}})": "", "Connection failed": "Échec de la connexion", + "Connection lost. Reconnecting...": "", "Connection successful": "Connexion réussie", "Connection Type": "Type de connexion", "Connections": "Connexions", @@ -526,6 +534,8 @@ "Delete All Chats": "Supprimer toutes les conversations", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Supprimer la Conversation", "Delete chat?": "Supprimer la conversation ?", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "Échec de la copie du lien", "Failed to create API Key.": "Échec de la création de la clé API.", + "Failed to delete calendar": "", "Failed to delete note": "Échec de la délétion de la note", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "Effort de raisonnement", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "Enregistrement", "Record voice": "Enregistrer la voix", "Redirecting you to Open WebUI Community": "Redirection vers la communauté OpenWebUI", @@ -1649,6 +1661,7 @@ "Relevance": "Pertinence", "Relevance Threshold": "Seuil de pertinence", "Remember Dismissal": "Se souvenir du rejet", + "Reminder": "", "Remove": "Retirer", "Remove {{MODELID}} from list.": "Retirer {{MODELID}} de la liste.", "Remove action": "", @@ -1896,7 +1909,12 @@ "Start a new conversation": "", "Start of the channel": "Début du canal", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "Cela supprimera {{NAME}} et tout son contenu.", "This will delete all models including custom models": "Cela supprimera tous les modèles, y compris les modèles personnalisés", "This will delete all models including custom models and cannot be undone.": "Cela supprimera tous les modèles, y compris les modèles personnalisés, et ne peut pas être annulé.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Cela réinitialisera la base de connaissances et synchronisera tous les fichiers. Souhaitez-vous continuer ?", "Thorough explanation": "Explication approfondie", "Thought": "", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "Décharge {{FROM_NOW}}", "Unlock mysteries": "Déverrouiller les mystères", "Unpin": "Désépingler", + "Unpin from Sidebar": "", "Unravel secrets": "Dévoiler les secrets", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/fr-FR/translation.json b/src/lib/i18n/locales/fr-FR/translation.json index 0de23b8898..4572e362c8 100644 --- a/src/lib/i18n/locales/fr-FR/translation.json +++ b/src/lib/i18n/locales/fr-FR/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "Conversations de {{user}}", "{{webUIName}} Backend Required": "Backend {{webUIName}} requis", "*Prompt node ID(s) are required for image generation": "*Les ID de noeud du prompt sont nécessaires pour la génération d'image", + "1 hour before": "", "1 Source": "1 Source", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "1min", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Un canal collaboratif où les membres rejoignent librement", "A discussion channel where access is controlled by groups and permissions": "Un canal de discussion où l'accès est contrôlé par les groupes et les permissions", "A new version (v{{LATEST_VERSION}}) is now available.": "Une nouvelle version (v{{LATEST_VERSION}}) est disponible.", @@ -203,6 +208,7 @@ "Ask a question": "Posez votre question", "Assistant": "Assistant", "Async Embedding Processing": "Traitement asynchrone des embeddings", + "At time of event": "", "Attach File From Knowledge": "Joindre un fichier depuis les connaissances", "Attach Files": "", "Attach Knowledge": "Joindre une connaissance", @@ -277,6 +283,7 @@ "Bypass Web Loader": "Ignorer le chargeur Web", "Cache Base Model List": "Mettre en cache la liste des modèles de base", "Calendar": "Calendrier", + "Calendar deleted": "", "Calendars": "", "Call": "Appeler", "Call feature is not supported when using Web STT engine": "La fonction d'appel n'est pas prise en charge lors de l'utilisation du moteur Web STT", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Connectez-vous à vos serveurs d'outils externes.", "Connected ({{type}})": "", "Connection failed": "Échec de la connexion", + "Connection lost. Reconnecting...": "", "Connection successful": "Connexion réussie", "Connection Type": "Type de connexion", "Connections": "Connexions", @@ -526,6 +534,8 @@ "Delete All Chats": "Supprimer toutes les conversations", "Delete all contents inside this folder": "Supprimer tout le contenu de ce dossier", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Supprimer la Conversation", "Delete chat?": "Supprimer la conversation ?", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "Échec de la connexion au serveur de terminal {{URL}}", "Failed to copy link": "Échec de la copie du lien", "Failed to create API Key.": "Échec de la création de la clé API.", + "Failed to delete calendar": "", "Failed to delete note": "Échec de la délétion de la note", "Failed to download image": "Échec du téléchargement de l'image", "Failed to extract content from the file: {{error}}": "Échec de l'extraction du contenu du fichier : {{error}}", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "Effort de raisonnement", "Reasoning Tags": "Balises de raisonnement", "Recently Used": "", + "Reconnected": "", "Record": "Enregistrement", "Record voice": "Enregistrer la voix", "Redirecting you to Open WebUI Community": "Redirection vers la communauté OpenWebUI", @@ -1649,6 +1661,7 @@ "Relevance": "Pertinence", "Relevance Threshold": "Seuil de pertinence", "Remember Dismissal": "Se souvenir du rejet", + "Reminder": "", "Remove": "Retirer", "Remove {{MODELID}} from list.": "Retirer {{MODELID}} de la liste.", "Remove action": "Retirer l'action", @@ -1896,7 +1909,12 @@ "Start a new conversation": "Démarrer une nouvelle conversation", "Start of the channel": "Début du canal", "Start Tag": "Balise de départ", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Démarrage du noyau...", + "Starting now": "", "State": "", "Status": "Statut", "Status cleared successfully": "Statut effacé avec succès", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "Cela supprimera {{NAME}} et tout son contenu.", "This will delete all models including custom models": "Cela supprimera tous les modèles, y compris les modèles personnalisés", "This will delete all models including custom models and cannot be undone.": "Cela supprimera tous les modèles, y compris les modèles personnalisés, et ne peut pas être annulé.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Cela réinitialisera la base de connaissances et synchronisera tous les fichiers. Souhaitez-vous continuer ?", "Thorough explanation": "Explication approfondie", "Thought": "", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "Décharge {{FROM_NOW}}", "Unlock mysteries": "Déverrouiller les mystères", "Unpin": "Désépingler", + "Unpin from Sidebar": "", "Unravel secrets": "Dévoiler les secrets", "Unshare Chat": "Annuler le partage de la conversation", "Unsupported file type.": "Type de fichier non pris en charge.", diff --git a/src/lib/i18n/locales/gl-ES/translation.json b/src/lib/i18n/locales/gl-ES/translation.json index 3c36e045e9..df434bfa1a 100644 --- a/src/lib/i18n/locales/gl-ES/translation.json +++ b/src/lib/i18n/locales/gl-ES/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "Chats do {{user}}", "{{webUIName}} Backend Required": "{{webUIName}} Servidor Requerido", "*Prompt node ID(s) are required for image generation": "Os ID do nodo son requeridos para a xeneración de imáxes", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Unha nova versión (v{{LATEST_VERSION}}) está disponible.", @@ -202,6 +207,7 @@ "Ask a question": "Fai unha pregunta", "Assistant": "Asistente", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "Chamada", "Call feature is not supported when using Web STT engine": "A funcionalidade da chamada non pode usarse xunto co motor da STT Web", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "Conexions", @@ -525,6 +533,8 @@ "Delete All Chats": "Eliminar todos os chats", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Borrar Chat", "Delete chat?": "Borrar o chat?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Non pudo xerarse a chave API.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Esfuerzo de razonamiento", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Grabar voz", "Redirecting you to Open WebUI Community": "Redireccionándote a a comunidad OpenWebUI", @@ -1648,6 +1660,7 @@ "Relevance": "Relevancia", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Eliminar", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "Inicio da canle", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Esto eliminará {{NAME}} y todo su contido.", "This will delete all models including custom models": "Esto eliminará todos os modelos, incluidos os modelos personalizados", "This will delete all models including custom models and cannot be undone.": "Esto eliminará todos os modelos, incluidos os modelos personalizados y no se puede deshacer.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Esto reseteará la base de coñecementos y sincronizará todos os arquivos. ¿Desea continuar?", "Thorough explanation": "Explicación exhaustiva", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "Desbloquear misterios", "Unpin": "Desanclar", + "Unpin from Sidebar": "", "Unravel secrets": "Desentrañar secretos", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/he-IL/translation.json b/src/lib/i18n/locales/he-IL/translation.json index 2bb98c4d4e..f36e6d332e 100644 --- a/src/lib/i18n/locales/he-IL/translation.json +++ b/src/lib/i18n/locales/he-IL/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "צ'אטים של {{user}}", "{{webUIName}} Backend Required": "נדרש Backend של {{webUIName}}", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -203,6 +208,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -277,6 +283,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "לוח שנה", + "Calendar deleted": "", "Calendars": "", "Call": "", "Call feature is not supported when using Web STT engine": "", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "החיבור נכשל", + "Connection lost. Reconnecting...": "", "Connection successful": "החיבור הצליח", "Connection Type": "סוג חיבור", "Connections": "חיבורים", @@ -526,6 +534,8 @@ "Delete All Chats": "מחק את כל הצ'אטים", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "מחק צ'אט", "Delete chat?": "", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "יצירת מפתח API נכשלה.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "הקלט קול", "Redirecting you to Open WebUI Community": "מפנה אותך לקהילת OpenWebUI", @@ -1649,6 +1661,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "הסר", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1896,7 +1909,12 @@ "Start a new conversation": "", "Start of the channel": "תחילת הערוץ", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_two": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "תיאור מפורט", "Thought": "", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/hi-IN/translation.json b/src/lib/i18n/locales/hi-IN/translation.json index ce96aa2286..eeeff64210 100644 --- a/src/lib/i18n/locales/hi-IN/translation.json +++ b/src/lib/i18n/locales/hi-IN/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}} की चैट", "{{webUIName}} Backend Required": "{{webUIName}} बैकएंड आवश्यक", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -202,6 +207,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "", "Call feature is not supported when using Web STT engine": "", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "सम्बन्ध", @@ -525,6 +533,8 @@ "Delete All Chats": "सभी चैट हटाएं", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "चैट हटाएं", "Delete chat?": "", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "एपीआई कुंजी बनाने में विफल.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "आवाज रिकॉर्ड करना", "Redirecting you to Open WebUI Community": "आपको OpenWebUI समुदाय पर पुनर्निर्देशित किया जा रहा है", @@ -1648,6 +1660,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "हटा दें", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "चैनल की शुरुआत", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "विस्तृत व्याख्या", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/hr-HR/translation.json b/src/lib/i18n/locales/hr-HR/translation.json index 01e9f0fdf1..c0525013e9 100644 --- a/src/lib/i18n/locales/hr-HR/translation.json +++ b/src/lib/i18n/locales/hr-HR/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "Razgovori korisnika {{user}}", "{{webUIName}} Backend Required": "{{webUIName}} Backend je potreban", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -203,6 +208,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -277,6 +283,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "Poziv", "Call feature is not supported when using Web STT engine": "Značajka poziva nije podržana kada se koristi Web STT mehanizam", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "Povezivanja", @@ -526,6 +534,8 @@ "Delete All Chats": "Izbriši sve razgovore", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Izbriši razgovor", "Delete chat?": "", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Neuspješno stvaranje API ključa.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Snimanje glasa", "Redirecting you to Open WebUI Community": "Preusmjeravanje na OpenWebUI zajednicu", @@ -1649,6 +1661,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Ukloni", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1896,7 +1909,12 @@ "Start a new conversation": "", "Start of the channel": "Početak kanala", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_few": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "Detaljno objašnjenje", "Thought": "", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/hu-HU/translation.json b/src/lib/i18n/locales/hu-HU/translation.json index 5d2fee4e33..22f4ea62cf 100644 --- a/src/lib/i18n/locales/hu-HU/translation.json +++ b/src/lib/i18n/locales/hu-HU/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}} beszélgetései", "{{webUIName}} Backend Required": "{{webUIName}} Backend szükséges", "*Prompt node ID(s) are required for image generation": "*Prompt node ID(k) szükségesek a képgeneráláshoz", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Új verzió (v{{LATEST_VERSION}}) érhető el.", @@ -202,6 +207,7 @@ "Ask a question": "Kérdezz valamit", "Assistant": "Asszisztens", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "Naptár", + "Calendar deleted": "", "Calendars": "", "Call": "Hívás", "Call feature is not supported when using Web STT engine": "A hívás funkció nem támogatott Web STT motor használatakor", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Csatlakozz saját OpenAPI kompatibilis külső eszköszervereidhez.", "Connected ({{type}})": "", "Connection failed": "Kapcsolat sikertelen", + "Connection lost. Reconnecting...": "", "Connection successful": "Kapcsolat sikeres", "Connection Type": "", "Connections": "Kapcsolatok", @@ -525,6 +533,8 @@ "Delete All Chats": "Minden beszélgetés törlése", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Beszélgetés törlése", "Delete chat?": "Törli a beszélgetést?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Nem sikerült létrehozni az API kulcsot.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Érvelési erőfeszítés", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Hang rögzítése", "Redirecting you to Open WebUI Community": "Átirányítás az OpenWebUI közösséghez", @@ -1648,6 +1660,7 @@ "Relevance": "Relevancia", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Eltávolítás", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "A csatorna eleje", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Ez törölni fogja a {{NAME}}-t és minden tartalmát.", "This will delete all models including custom models": "Ez törölni fogja az összes modellt, beleértve az egyéni modelleket is", "This will delete all models including custom models and cannot be undone.": "Ez törölni fogja az összes modellt, beleértve az egyéni modelleket is, és nem vonható vissza.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Ez visszaállítja a tudásbázist és szinkronizálja az összes fájlt. Szeretné folytatni?", "Thorough explanation": "Alapos magyarázat", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "Titkok feloldása", "Unpin": "Rögzítés feloldása", + "Unpin from Sidebar": "", "Unravel secrets": "Titkok megfejtése", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/id-ID/translation.json b/src/lib/i18n/locales/id-ID/translation.json index 2e60de3ed1..c537fb24bb 100644 --- a/src/lib/i18n/locales/id-ID/translation.json +++ b/src/lib/i18n/locales/id-ID/translation.json @@ -32,8 +32,13 @@ "{{user}}'s Chats": "Obrolan {{user}}", "{{webUIName}} Backend Required": "{{webUIName}} Diperlukan Backend", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -201,6 +206,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -275,6 +281,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "Panggilan", "Call feature is not supported when using Web STT engine": "Fitur panggilan tidak didukung saat menggunakan mesin Web STT", @@ -412,6 +419,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "Koneksi", @@ -524,6 +532,8 @@ "Delete All Chats": "Menghapus Semua Obrolan", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Menghapus Obrolan", "Delete chat?": "Menghapus obrolan?", "Delete Event": "", @@ -885,6 +895,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Gagal membuat API Key.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1624,6 +1635,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Rekam suara", "Redirecting you to Open WebUI Community": "Mengarahkan Anda ke Komunitas OpenWebUI", @@ -1647,6 +1659,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Hapus", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1892,7 +1905,10 @@ "Start a new conversation": "", "Start of the channel": "Awal saluran", "Start Tag": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2005,6 +2021,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "Penjelasan menyeluruh", "Thought": "", @@ -2091,6 +2108,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/ie-GA/translation.json b/src/lib/i18n/locales/ie-GA/translation.json index df9257c7e9..e5550ac069 100644 --- a/src/lib/i18n/locales/ie-GA/translation.json +++ b/src/lib/i18n/locales/ie-GA/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "Comhráite {{user}}", "{{webUIName}} Backend Required": "{{webUIName}} Ceoldeireadh Riachtanach", "*Prompt node ID(s) are required for image generation": "* Tá ID(anna) nód treorach ag teastáil chun íomhá a ghiniúint", + "1 hour before": "", "1 Source": "1 Foinse", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "1 nóiméad ó shin", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Cainéal comhoibrithe ina mbíonn daoine ag glacadh páirte mar bhaill", "A discussion channel where access is controlled by groups and permissions": "Cainéal plé ina bhfuil rochtain rialaithe ag grúpaí agus ceadanna", "A new version (v{{LATEST_VERSION}}) is now available.": "Tá leagan nua (v {{LATEST_VERSION}}) ar fáil anois.", @@ -202,6 +207,7 @@ "Ask a question": "Cuir ceist", "Assistant": "Cúntóir", "Async Embedding Processing": "Próiseáil Leabaithe Asyncrónach", + "At time of event": "", "Attach File From Knowledge": "Ceangail Comhad ó Eolas", "Attach Files": "Ceangail Comhaid", "Attach Knowledge": "Ceangail Eolas", @@ -276,6 +282,7 @@ "Bypass Web Loader": "Seachbhóthar Luchtaire Gréasáin", "Cache Base Model List": "Liosta Samhail Bunáite Taisce", "Calendar": "Féilire", + "Calendar deleted": "", "Calendars": "", "Call": "Glaoigh", "Call feature is not supported when using Web STT engine": "Ní thacaítear le gné glaonna agus inneall Web STT á úsáid", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Ceangail le do fhreastalaithe uirlisí seachtracha atá comhoiriúnach le OpenAPI.", "Connected ({{type}})": "Ceangailte ({{type}})", "Connection failed": "Theip ar an gceangal", + "Connection lost. Reconnecting...": "", "Connection successful": "Ceangal rathúil", "Connection Type": "Cineál Ceangail", "Connections": "Naisc", @@ -525,6 +533,8 @@ "Delete All Chats": "Scrios Gach Comhrá", "Delete all contents inside this folder": "Scrios an t-ábhar go léir atá sa fhillteán seo", "Delete automation?": "Scrios an t-uathoibriú?", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Scrios Comhrá", "Delete chat?": "Scrios comhrá?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "Theip ar cheangal le freastalaí críochfoirt {{URL}}", "Failed to copy link": "Theip ar an nasc a chóipeáil", "Failed to create API Key.": "Theip ar an eochair API a chruthú.", + "Failed to delete calendar": "", "Failed to delete note": "Theip ar an nóta a scriosadh", "Failed to download image": "Theip ar an íomhá a íoslódáil", "Failed to extract content from the file: {{error}}": "Theip ar an ábhar a bhaint as an gcomhad: {{error}}", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Iarracht Réasúnúcháin", "Reasoning Tags": "Clibeanna Réasúnaíochta", "Recently Used": "Úsáidte le Déanaí", + "Reconnected": "", "Record": "Taifead", "Record voice": "Taifead guth", "Redirecting you to Open WebUI Community": "Tú a atreorú chuig OpenWebUI Community", @@ -1648,6 +1660,7 @@ "Relevance": "Ábharthacht", "Relevance Threshold": "Tairseach Ábharthaíochta", "Remember Dismissal": "Cuimhnigh ar an Dífhostú", + "Reminder": "", "Remove": "Bain", "Remove {{MODELID}} from list.": "Bain {{MODELID}} den liosta.", "Remove action": "Bain gníomh", @@ -1894,7 +1907,11 @@ "Start a new conversation": "Tosaigh comhrá nua", "Start of the channel": "Tús an chainéil", "Start Tag": "Clib Tosaigh", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Ag tosú an eithne...", + "Starting now": "", "State": "Stát", "Status": "Stádas", "Status cleared successfully": "Glanadh an stádais go rathúil", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Scriosfaidh sé seo {{NAME}} agus a bhfuil ann go léir.", "This will delete all models including custom models": "Scriosfaidh sé seo gach samhail lena n-áirítear samhlacha saincheaptha", "This will delete all models including custom models and cannot be undone.": "Scriosfaidh sé seo gach samhail, lena n-áirítear samhlacha saincheaptha, agus ní féidir é a chealú.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Déanfaidh sé seo an bonn eolais a athshocrú agus gach comhad a shioncronú. Ar mhaith leat leanúint ar aghaidh?", "Thorough explanation": "Míniú críochnúil", "Thought": "Smaoineamh", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "Díluchtuithe {{FROM_NOW}}", "Unlock mysteries": "Díghlasáil rúndiamhra", "Unpin": "Díphoráil", + "Unpin from Sidebar": "", "Unravel secrets": "Rúin a réiteach", "Unshare Chat": "Díroinn Comhrá", "Unsupported file type.": "Cineál comhaid nach dtacaítear leis.", diff --git a/src/lib/i18n/locales/it-IT/translation.json b/src/lib/i18n/locales/it-IT/translation.json index e8b2b0f217..c94b0f7f5a 100644 --- a/src/lib/i18n/locales/it-IT/translation.json +++ b/src/lib/i18n/locales/it-IT/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "{{user}} Chat", "{{webUIName}} Backend Required": "{{webUIName}} Richiesta Backend", "*Prompt node ID(s) are required for image generation": "*ID nodo prompt sono necessari per la generazione di immagini", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Una nuova versione (v{{LATEST_VERSION}}) è ora disponibile.", @@ -203,6 +208,7 @@ "Ask a question": "Fai una domanda", "Assistant": "Assistente", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -277,6 +283,7 @@ "Bypass Web Loader": "Bypassa il Web Loader", "Cache Base Model List": "", "Calendar": "Calendario", + "Calendar deleted": "", "Calendars": "", "Call": "Chiamata", "Call feature is not supported when using Web STT engine": "La funzione di chiamata non è supportata quando si utilizza il motore Web STT", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Connettiti ai tuoi server di tool esterni compatibili con OpenAPI.", "Connected ({{type}})": "", "Connection failed": "Connessione fallita", + "Connection lost. Reconnecting...": "", "Connection successful": "Connessione riuscita", "Connection Type": "Tipo Connessione", "Connections": "Connessioni", @@ -526,6 +534,8 @@ "Delete All Chats": "Elimina tutte le chat", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Elimina chat", "Delete chat?": "Elimina chat?", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "Impossibile copiare il link", "Failed to create API Key.": "Impossibile creare Chiave API.", + "Failed to delete calendar": "", "Failed to delete note": "Impossibile eliminare la nota", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "Sforzo di ragionamento", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "Registra", "Record voice": "Registra voce", "Redirecting you to Open WebUI Community": "Reindirizzamento alla comunità OpenWebUI", @@ -1649,6 +1661,7 @@ "Relevance": "Rilevanza", "Relevance Threshold": "Soglia di Rilevanza", "Remember Dismissal": "", + "Reminder": "", "Remove": "Rimuovi", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1896,7 +1909,12 @@ "Start a new conversation": "", "Start of the channel": "Inizio del canale", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "Questa opzione eliminerà {{NAME}} e tutti i suoi contenuti.", "This will delete all models including custom models": "Questa opzione eliminerà tutti i modelli, compresi i modelli personalizzati", "This will delete all models including custom models and cannot be undone.": "Questa opzione eliminerà tutti i modelli, compresi i modelli personalizzati e non può essere annullata.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Questa opzione ripristinerà la base di conoscenza e sincronizzerà tutti i file. Vuoi continuare?", "Thorough explanation": "Spiegazione dettagliata", "Thought": "", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "Scarica {{FROM_NOW}}", "Unlock mysteries": "Sblocca misteri", "Unpin": "Rimuovi fissato", + "Unpin from Sidebar": "", "Unravel secrets": "Svela segreti", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/ja-JP/translation.json b/src/lib/i18n/locales/ja-JP/translation.json index 77aa239d44..af0c4211b5 100644 --- a/src/lib/i18n/locales/ja-JP/translation.json +++ b/src/lib/i18n/locales/ja-JP/translation.json @@ -32,8 +32,13 @@ "{{user}}'s Chats": "{{user}} のチャット", "{{webUIName}} Backend Required": "{{webUIName}} バックエンドが必要です", "*Prompt node ID(s) are required for image generation": "*画像生成にはプロンプトノードIDが必要です", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "新しいバージョン (v{{LATEST_VERSION}}) が利用可能です。", @@ -201,6 +206,7 @@ "Ask a question": "質問する", "Assistant": "アシスタント", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "ナレッジからファイルを添付", "Attach Files": "ファイルを追加", "Attach Knowledge": "ナレッジを追加", @@ -275,6 +281,7 @@ "Bypass Web Loader": "Webローダーをバイパス", "Cache Base Model List": "ベースモデルリストをキャッシュ", "Calendar": "カレンダー", + "Calendar deleted": "", "Calendars": "", "Call": "コール", "Call feature is not supported when using Web STT engine": "Web STTエンジンを使用している場合、コール機能は使用できません", @@ -412,6 +419,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "独自のOpenAPI互換外部ツールサーバーに接続します。", "Connected ({{type}})": "", "Connection failed": "接続に失敗しました", + "Connection lost. Reconnecting...": "", "Connection successful": "接続に成功しました", "Connection Type": "接続タイプ", "Connections": "接続", @@ -524,6 +532,8 @@ "Delete All Chats": "すべてのチャットを削除", "Delete all contents inside this folder": "", "Delete automation?": "オートメーションを削除しますか?", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "チャットを削除", "Delete chat?": "チャットを削除しますか?", "Delete Event": "", @@ -885,6 +895,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "リンクのコピーに失敗しました。", "Failed to create API Key.": "APIキーの作成に失敗しました。", + "Failed to delete calendar": "", "Failed to delete note": "ノートの削除に失敗しました。", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "ファイルから中身の取得に失敗しました: {{error}}", @@ -1624,6 +1635,7 @@ "Reasoning Effort": "推理の努力", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "録音", "Record voice": "音声を録音", "Redirecting you to Open WebUI Community": "OpenWebUI コミュニティにリダイレクトしています", @@ -1647,6 +1659,7 @@ "Relevance": "関連性", "Relevance Threshold": "関連性の閾値", "Remember Dismissal": "閉じたことを記憶する", + "Reminder": "", "Remove": "削除", "Remove {{MODELID}} from list.": "{{MODELID}} をリストから削除する", "Remove action": "", @@ -1892,7 +1905,10 @@ "Start a new conversation": "新しい会話を開始", "Start of the channel": "チャンネルの開始", "Start Tag": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "状態", "Status": "ステータス", "Status cleared successfully": "正常にステータスをクリアしました", @@ -2005,6 +2021,7 @@ "This will delete {{NAME}} and all its contents.": "これは{{NAME}}とそのすべての内容を削除します。", "This will delete all models including custom models": "これはカスタムモデルを含むすべてのモデルを削除します", "This will delete all models including custom models and cannot be undone.": "これはカスタムモデルを含むすべてのモデルを削除し、元に戻すことはできません。", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "これは知識ベースをリセットし、すべてのファイルを同期します。続けますか?", "Thorough explanation": "詳細な説明", "Thought": "", @@ -2091,6 +2108,7 @@ "Unloads {{FROM_NOW}}": "{{FROM_NOW}}にアンロード", "Unlock mysteries": "ミステリーを解き明かす", "Unpin": "ピン留め解除", + "Unpin from Sidebar": "", "Unravel secrets": "秘密を解き明かす", "Unshare Chat": "", "Unsupported file type.": "未対応のファイルタイプです", diff --git a/src/lib/i18n/locales/ka-GE/translation.json b/src/lib/i18n/locales/ka-GE/translation.json index b2726bf790..acdc14db3c 100644 --- a/src/lib/i18n/locales/ka-GE/translation.json +++ b/src/lib/i18n/locales/ka-GE/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}}-ის ჩათები", "{{webUIName}} Backend Required": "{{webUIName}} საჭიროა უკანაბოლო", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "1 წყარო", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "ხელმისაწვდომია ახალი ვერსია (v{{LATEST_VERSION}}).", @@ -202,6 +207,7 @@ "Ask a question": "კითხვის დასმა", "Assistant": "დამხმარე", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "ცოდნის მიმაგრება", @@ -276,6 +282,7 @@ "Bypass Web Loader": "ვებჩამტვირთავის გამოტოვება", "Cache Base Model List": "საბაზისო მოდელების სიის დაკეშვა", "Calendar": "კალენდარი", + "Calendar deleted": "", "Calendars": "", "Call": "ზარი", "Call feature is not supported when using Web STT engine": "", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "დაკავშირება ვერ მოხერხდა", + "Connection lost. Reconnecting...": "", "Connection successful": "შეერთება წარმატებულია", "Connection Type": "შეერთების ტიპი", "Connections": "კავშირები", @@ -525,6 +533,8 @@ "Delete All Chats": "ყველა ჩატის წაშლა", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "საუბრის წაშლა", "Delete chat?": "წავშალო ჩატი?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "ბმულის კოპირება ჩავარდა", "Failed to create API Key.": "API-ის გასაღების შექმნა ჩავარდა.", + "Failed to delete calendar": "", "Failed to delete note": "შენიშვნის წაშლა ჩავარდა", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "ჩაწერა", "Record voice": "ხმის ჩაწერა", "Redirecting you to Open WebUI Community": "მიმდინარეობს გადამისამართება OpenWebUI-ის საზოგადოების საიტზე", @@ -1648,6 +1660,7 @@ "Relevance": "შესაბამისობა", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "წაშლა", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "არხის დასაწყისი", "Start Tag": "დაწყების ჭდე", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "საფუძვლიანი ახსნა", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "გამოტვირთვა {{FROM_NOW}}", "Unlock mysteries": "", "Unpin": "ჩამოხსნა", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/kab-DZ/translation.json b/src/lib/i18n/locales/kab-DZ/translation.json index 4d0c32b8d6..0fc3787813 100644 --- a/src/lib/i18n/locales/kab-DZ/translation.json +++ b/src/lib/i18n/locales/kab-DZ/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "Asqerdec n {{user}}", "{{webUIName}} Backend Required": "", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "1 n weɣbalu", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Lqem amaynut n (v{{LATEST_VERSION}}), yella akka tura.", @@ -202,6 +207,7 @@ "Ask a question": "Efk-d asteqsi", "Assistant": "Amallal", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "Qqen-as tamessunt", @@ -276,6 +282,7 @@ "Bypass Web Loader": "Zgel asalay Web", "Cache Base Model List": "Ffer tabdart n tmudmiwin n taffa", "Calendar": "Awitay", + "Calendar deleted": "", "Calendars": "", "Call": "Siwel", "Call feature is not supported when using Web STT engine": "Tamahilt n usiwel ur tettwasefrak ara mi ara tesqedceḍ amsedday Web STT", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Qqen ɣer yiqeddacen-ik n yifecka imeṛṛa yeldin.", "Connected ({{type}})": "", "Connection failed": "Tuqqna d-tawezɣit", + "Connection lost. Reconnecting...": "", "Connection successful": "Tuqqna tedda akken iwata", "Connection Type": "Anaw n tuqqna", "Connections": "Tuqqniwin", @@ -525,6 +533,8 @@ "Delete All Chats": "Kkes akk idiwenniyen", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Kkes asqerdec", "Delete chat?": "Tebɣiḍ ad tekkseḍ adiwenni?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "Ur yessaweḍ ara ad yessukken aseɣwen", "Failed to create API Key.": "Ur yessaweḍ ara ad d-yesnulfu tasarut API.", + "Failed to delete calendar": "", "Failed to delete note": "Ur yessaweḍ ara ad yekkes tazmilt", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "Ur yessaweḍ ara ad d-yekkes agbur seg ufaylu: {{error}}", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "Aklas", "Record voice": "Sekles taɣect", "Redirecting you to Open WebUI Community": "Aseḍfeṛ ar Temɣiwant n Open WebUI", @@ -1648,6 +1660,7 @@ "Relevance": "Tawatit", "Relevance Threshold": "", "Remember Dismissal": "Ccfawa ɣef ugdal", + "Reminder": "", "Remove": "Kkes", "Remove {{MODELID}} from list.": "Kkes {{MODELID}} seg wumuɣ.", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "Tazwara n ubadu", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Aya ad yekkes {NAME}} akked akk ayen yellan deg-s.", "This will delete all models including custom models": "Aya ad yekkes akk timudmin yellan gar-asent timudmin n tannumi", "This will delete all models including custom models and cannot be undone.": "Aya ad yekkes akk timudmin gar-asent timudmin tudmawanin yerna ur yezmir yiwen ad tent-id-yerr.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Aya ad yales taffa n tmussni u ad yemtawi akk ifuyla. Tebɣiḍ ad tkemmleḍ?", "Thorough explanation": "Asegzi leqqayen", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "Kkes asenteḍ", + "Unpin from Sidebar": "", "Unravel secrets": "Sban-d ayen yeffren", "Unshare Chat": "", "Unsupported file type.": "Tawsit n ufaylu ur tettusefrak ara.", diff --git a/src/lib/i18n/locales/ko-KR/translation.json b/src/lib/i18n/locales/ko-KR/translation.json index 2c4d22b843..e29c402508 100644 --- a/src/lib/i18n/locales/ko-KR/translation.json +++ b/src/lib/i18n/locales/ko-KR/translation.json @@ -32,8 +32,13 @@ "{{user}}'s Chats": "{{user}}의 채팅", "{{webUIName}} Backend Required": "{{webUIName}} 백엔드가 필요합니다.", "*Prompt node ID(s) are required for image generation": "이미지 생성에는 프롬프트 노드 ID가 필요합니다.", + "1 hour before": "", "1 Source": "소스1", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "새로운 버전 (v{{LATEST_VERSION}})을 사용할 수 있습니다.", @@ -201,6 +206,7 @@ "Ask a question": "질문하기", "Assistant": "어시스턴트", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "지식 기반에서 파일 첨부", "Attach Files": "", "Attach Knowledge": "지식 기반 첨부", @@ -275,6 +281,7 @@ "Bypass Web Loader": "웹 콘텐츠 불러오기 생략", "Cache Base Model List": "기본 모델 목록 캐시", "Calendar": "캘린더", + "Calendar deleted": "", "Calendars": "", "Call": "음성 기능", "Call feature is not supported when using Web STT engine": "웹 STT 엔진 사용 시, 음성 기능은 지원되지 않습니다.", @@ -412,6 +419,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "OpenAPI 호환 외부 도구 서버에 연결합니다.", "Connected ({{type}})": "", "Connection failed": "연결 실패", + "Connection lost. Reconnecting...": "", "Connection successful": "연결 성공", "Connection Type": "연결 방식", "Connections": "연결", @@ -524,6 +532,8 @@ "Delete All Chats": "모든 채팅 삭제", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "채팅 삭제", "Delete chat?": "채팅을 삭제하시겠습니까?", "Delete Event": "", @@ -885,6 +895,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "링크 복사 실패", "Failed to create API Key.": "API 키 생성에 실패했습니다.", + "Failed to delete calendar": "", "Failed to delete note": "노트 삭제 실패", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "파일 내용 추출 실패: {{error}}", @@ -1624,6 +1635,7 @@ "Reasoning Effort": "추론 난이도", "Reasoning Tags": "추론 태그", "Recently Used": "", + "Reconnected": "", "Record": "녹음", "Record voice": "음성 녹음", "Redirecting you to Open WebUI Community": "OpenWebUI 커뮤니티로 리디렉션 중", @@ -1647,6 +1659,7 @@ "Relevance": "관련도", "Relevance Threshold": "관련성 임계값", "Remember Dismissal": "다시 보지 않기", + "Reminder": "", "Remove": "삭제", "Remove {{MODELID}} from list.": "{{MODELID}}를 목록에서 제거.", "Remove action": "", @@ -1892,7 +1905,10 @@ "Start a new conversation": "새 대화 시작", "Start of the channel": "채널 시작", "Start Tag": "시작 태그", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "상태", "Status cleared successfully": "상태 초기화에 성공했습니다", @@ -2005,6 +2021,7 @@ "This will delete {{NAME}} and all its contents.": "{{NAME}}모든 내용을 삭제합니다.", "This will delete all models including custom models": "이렇게 하면 사용자 지정 모델을 포함한 모든 모델이 삭제됩니다", "This will delete all models including custom models and cannot be undone.": "이렇게 하면 사용자 지정 모델을 포함한 모든 모델이 삭제되며 실행 취소할 수 없습니다.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "지식 기반과 모든 파일 연동을 초기화합니다. 계속 하시겠습니까?", "Thorough explanation": "완전한 설명", "Thought": "", @@ -2091,6 +2108,7 @@ "Unloads {{FROM_NOW}}": "{{FROM_NOW}} 언로드", "Unlock mysteries": "미스터리 풀기", "Unpin": "고정 해제", + "Unpin from Sidebar": "", "Unravel secrets": "비밀 풀기", "Unshare Chat": "", "Unsupported file type.": "지원하지 않는 파일 형식", diff --git a/src/lib/i18n/locales/lt-LT/translation.json b/src/lib/i18n/locales/lt-LT/translation.json index 0f2df1e48d..784832cde7 100644 --- a/src/lib/i18n/locales/lt-LT/translation.json +++ b/src/lib/i18n/locales/lt-LT/translation.json @@ -35,8 +35,13 @@ "{{user}}'s Chats": "{{user}} susirašinėjimai", "{{webUIName}} Backend Required": "{{webUIName}} būtinas serveris", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -204,6 +209,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -278,6 +284,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "Skambinti", "Call feature is not supported when using Web STT engine": "Skambučio funkcionalumas neleidžiamas naudojant Web STT variklį", @@ -415,6 +422,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "Ryšiai", @@ -527,6 +535,8 @@ "Delete All Chats": "Ištrinti visus pokalbius", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Ištrinti pokalbį", "Delete chat?": "Ištrinti pokalbį?", "Delete Event": "", @@ -888,6 +898,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Nepavyko sukurti API rakto", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1627,6 +1638,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Įrašyti balsą", "Redirecting you to Open WebUI Community": "Perkeliam Jus į OpenWebUI bendruomenę", @@ -1650,6 +1662,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Pašalinti", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1898,7 +1911,13 @@ "Start a new conversation": "", "Start of the channel": "Kanalo pradžia", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_few": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2011,6 +2030,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "Platus paaiškinimas", "Thought": "", @@ -2097,6 +2117,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "Atsemigti", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/lv-LV/translation.json b/src/lib/i18n/locales/lv-LV/translation.json index 9b28e934cf..09ae34c6a3 100644 --- a/src/lib/i18n/locales/lv-LV/translation.json +++ b/src/lib/i18n/locales/lv-LV/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "{{user}} tērzēšanas", "{{webUIName}} Backend Required": "Nepieciešama {{webUIName}} aizmugursistēma", "*Prompt node ID(s) are required for image generation": "*Attēla ģenerēšanai nepieciešami uzvednes mezgla ID", + "1 hour before": "", "1 Source": "1 avots", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Sadarbības kanāls, kurā cilvēki pievienojas kā dalībnieki", "A discussion channel where access is controlled by groups and permissions": "Diskusiju kanāls, kur piekļuvi kontrolē grupas un atļaujas", "A new version (v{{LATEST_VERSION}}) is now available.": "Ir pieejama jauna versija (v{{LATEST_VERSION}}).", @@ -203,6 +208,7 @@ "Ask a question": "Uzdot jautājumu", "Assistant": "Asistents", "Async Embedding Processing": "Asinhronā iegulšanas apstrāde", + "At time of event": "", "Attach File From Knowledge": "Pievienot failu no zināšanām", "Attach Files": "", "Attach Knowledge": "Pievienot zināšanau bāzi", @@ -277,6 +283,7 @@ "Bypass Web Loader": "Apiet tīmekļa ielādētāju", "Cache Base Model List": "Kešot bāzes modeļu sarakstu", "Calendar": "Kalendārs", + "Calendar deleted": "", "Calendars": "", "Call": "Zvans", "Call feature is not supported when using Web STT engine": "Zvana funkcija nav atbalstīta, izmantojot Web STT dzinēju", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Savienojieties ar saviem OpenAPI saderīgajiem ārējo rīku serveriem.", "Connected ({{type}})": "", "Connection failed": "Savienojums neizdevās", + "Connection lost. Reconnecting...": "", "Connection successful": "Savienojums veiksmīgs", "Connection Type": "Savienojuma tips", "Connections": "Savienojumi", @@ -526,6 +534,8 @@ "Delete All Chats": "Dzēst visas tērzēšanas", "Delete all contents inside this folder": "Dzēst visu saturu šajā mapē", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Dzēst tērzēšanu", "Delete chat?": "Dzēst tērzēšanu?", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "Neizdevās nokopēt saiti", "Failed to create API Key.": "Neizdevās izveidot API atslēgu.", + "Failed to delete calendar": "", "Failed to delete note": "Neizdevās dzēst piezīmi", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "Neizdevās ekstrahēt saturu no faila: {{error}}", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "Spriedumu pūles", "Reasoning Tags": "Spriedumu tagi", "Recently Used": "", + "Reconnected": "", "Record": "Ierakstīt", "Record voice": "Ierakstīt balsi", "Redirecting you to Open WebUI Community": "Novirza jūs uz Open WebUI kopienu", @@ -1649,6 +1661,7 @@ "Relevance": "Atbilstība", "Relevance Threshold": "Atbilstības slieksnis", "Remember Dismissal": "Atcerēties noraidījumu", + "Reminder": "", "Remove": "Noņemt", "Remove {{MODELID}} from list.": "Noņemt {{MODELID}} no saraksta.", "Remove action": "", @@ -1896,7 +1909,12 @@ "Start a new conversation": "Sākt jaunu sarunu", "Start of the channel": "Kanāla sākums", "Start Tag": "Sākuma tags", + "Starting in {{count}} minutes_zero": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "Statuss", "Status cleared successfully": "Statuss veiksmīgi notīrīts", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "Tas dzēsīs {{NAME}} un visu tā saturu.", "This will delete all models including custom models": "Tas dzēsīs visus modeļus, ieskaitot pielāgotos modeļus", "This will delete all models including custom models and cannot be undone.": "Tas dzēsīs visus modeļus, ieskaitot pielāgotos modeļus, un to nevar atsaukt.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Tas atiestatīs zināšanu bāzi un sinhronizēs visus failus. Vai vēlaties turpināt?", "Thorough explanation": "Pamatīgs skaidrojums", "Thought": "", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "Izlādēs {{FROM_NOW}}", "Unlock mysteries": "Atklājiet noslēpumus", "Unpin": "Atspraust", + "Unpin from Sidebar": "", "Unravel secrets": "Atšķetiniet noslēpumus", "Unshare Chat": "", "Unsupported file type.": "Neatbalstīts faila tips.", diff --git a/src/lib/i18n/locales/ms-MY/translation.json b/src/lib/i18n/locales/ms-MY/translation.json index 7f23e7ed77..8dd75ac052 100644 --- a/src/lib/i18n/locales/ms-MY/translation.json +++ b/src/lib/i18n/locales/ms-MY/translation.json @@ -32,8 +32,13 @@ "{{user}}'s Chats": "Perbualan {{user}}", "{{webUIName}} Backend Required": "{{webUIName}} Backend diperlukan", "*Prompt node ID(s) are required for image generation": "*ID nod Prompt diperlukan untuk penjanaan imej", + "1 hour before": "", "1 Source": "1 Sumber", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "1m_masa_lalu", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Saluran kolaborasi di mana orang ramai menyertai sebagai ahli", "A discussion channel where access is controlled by groups and permissions": "Saluran perbincangan di mana akses dikawal oleh kumpulan dan kebenaran", "A new version (v{{LATEST_VERSION}}) is now available.": "Versi baru (v{{LATEST_VERSION}}) kini tersedia.", @@ -201,6 +206,7 @@ "Ask a question": "Tanya soalan", "Assistant": "Pembantu", "Async Embedding Processing": "Pemprosesan Embedding Tak Segerak", + "At time of event": "", "Attach File From Knowledge": "Lampirkan Fail Daripada Pengetahuan", "Attach Files": "", "Attach Knowledge": "Lampirkan Pengetahuan", @@ -275,6 +281,7 @@ "Bypass Web Loader": "Langkau Pemuat Web", "Cache Base Model List": "Senarai Model Asas Cache", "Calendar": "Kalendar", + "Calendar deleted": "", "Calendars": "", "Call": "Hubungi", "Call feature is not supported when using Web STT engine": "Ciri panggilan tidak disokong apabila menggunakan enjin Web STT", @@ -412,6 +419,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Sambung ke pelayan alat luaran yang serasi dengan OpenAPI anda sendiri.", "Connected ({{type}})": "", "Connection failed": "Sambungan gagal", + "Connection lost. Reconnecting...": "", "Connection successful": "Sambungan berjaya", "Connection Type": "Jenis Sambungan", "Connections": "Sambungan", @@ -524,6 +532,8 @@ "Delete All Chats": "Padam Semua Perbualan", "Delete all contents inside this folder": "Padam semua kandungan dalam folder ini", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Padam Perbualan", "Delete chat?": "Padam perbualan?", "Delete Event": "", @@ -885,6 +895,7 @@ "Failed to connect to {{URL}} terminal server": "Gagal menyambung ke pelayan terminal {{URL}}", "Failed to copy link": "Gagal menyalin pautan", "Failed to create API Key.": "Gagal mencipta kekunci API", + "Failed to delete calendar": "", "Failed to delete note": "Gagal memadamkan nota", "Failed to download image": "Gagal memuat turun imej", "Failed to extract content from the file: {{error}}": "Gagal mengekstrak kandungan daripada fail: {{error}}", @@ -1624,6 +1635,7 @@ "Reasoning Effort": "Usaha Penaakulan", "Reasoning Tags": "Tag Penaakulan", "Recently Used": "", + "Reconnected": "", "Record": "Rakaman", "Record voice": "Rakam suara", "Redirecting you to Open WebUI Community": "Membawa anda ke Komuniti OpenWebUI", @@ -1647,6 +1659,7 @@ "Relevance": "Perkaitan", "Relevance Threshold": "Ambang Perkaitan", "Remember Dismissal": "Ingat Penutupan", + "Reminder": "", "Remove": "Hapuskan", "Remove {{MODELID}} from list.": "Keluarkan {{MODELID}} daripada senarai.", "Remove action": "Keluarkan tindakan", @@ -1892,7 +1905,10 @@ "Start a new conversation": "Mulai perbualan baru", "Start of the channel": "Permulaan saluran", "Start Tag": "Tag Permulaan", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Kernel sedang dimulakan...", + "Starting now": "", "State": "", "Status": "Status", "Status cleared successfully": "Status telah dihapus dengan berjaya", @@ -2005,6 +2021,7 @@ "This will delete {{NAME}} and all its contents.": "Ini akan memadam {{NAME}} dan semua kandungannya.", "This will delete all models including custom models": "Ini akan memadam semua model termasuk model tersuai", "This will delete all models including custom models and cannot be undone.": "Ini akan memadam semua model termasuk model tersuai dan tidak boleh dibuat asal.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Ini akan menetapkan semula pangkalan pengetahuan dan menyegerakkan semua fail. Adakah anda ingin meneruskan?", "Thorough explanation": "Penjelasan menyeluruh", "Thought": "", @@ -2091,6 +2108,7 @@ "Unloads {{FROM_NOW}}": "Membuang {{FROM_NOW}}", "Unlock mysteries": "Buka Misteri", "Unpin": "Nyahsematkan", + "Unpin from Sidebar": "", "Unravel secrets": "Ungkap Rahsia", "Unshare Chat": "Batalkan Perkongsian Sembang", "Unsupported file type.": "Jenis fail tidak disokong.", diff --git a/src/lib/i18n/locales/nb-NO/translation.json b/src/lib/i18n/locales/nb-NO/translation.json index ebfff87340..7f45c82157 100644 --- a/src/lib/i18n/locales/nb-NO/translation.json +++ b/src/lib/i18n/locales/nb-NO/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}} sine samtaler", "{{webUIName}} Backend Required": "Backend til {{webUIName}} kreves", "*Prompt node ID(s) are required for image generation": "Node-ID-er for ledetekst kreves for generering av bilder", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "En ny versjon (v{{LATEST_VERSION}}) er nå tilgjengelig.", @@ -202,6 +207,7 @@ "Ask a question": "Still et spørsmål", "Assistant": "Assistent", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "Kalender", + "Calendar deleted": "", "Calendars": "", "Call": "Ring", "Call feature is not supported when using Web STT engine": "Ringefunksjonen støttes ikke når du bruker Web STT-motoren", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "Tilkoblinger", @@ -525,6 +533,8 @@ "Delete All Chats": "Slett alle chatter", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Slett chat", "Delete chat?": "Slette chat?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Kan ikke opprette en API-nøkkel.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Resonneringsinnsats", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Ta opp tale", "Redirecting you to Open WebUI Community": "Omdirigerer deg til OpenWebUI-fellesskapet", @@ -1648,6 +1660,7 @@ "Relevance": "Relevans", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Fjern", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "Starten av kanalen", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Dette sletter {{NAME}} og alt innholdet.", "This will delete all models including custom models": "Dette sletter alle modeller, inkludert tilpassede modeller", "This will delete all models including custom models and cannot be undone.": "Dette sletter alle modeller, inkludert tilpassede modeller, og kan ikke angres.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Dette tilbakestiller kunnskapsbasen og synkroniserer alle filer. Vil du fortsette?", "Thorough explanation": "Grundig forklaring", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "Lås opp mysterier", "Unpin": "Løsne", + "Unpin from Sidebar": "", "Unravel secrets": "Avslør hemmeligheter", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/nl-NL/translation.json b/src/lib/i18n/locales/nl-NL/translation.json index 3458ccf314..4651d33fc5 100644 --- a/src/lib/i18n/locales/nl-NL/translation.json +++ b/src/lib/i18n/locales/nl-NL/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}}'s chats", "{{webUIName}} Backend Required": "{{webUIName}} Backend verplicht", "*Prompt node ID(s) are required for image generation": "*Prompt node ID('s) zijn vereist voor het genereren van afbeeldingen", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Een nieuwe versie (v{{LATEST_VERSION}}) is nu beschikbaar", @@ -202,6 +207,7 @@ "Ask a question": "Stel een vraag", "Assistant": "Assistent", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "Agenda", + "Calendar deleted": "", "Calendars": "", "Call": "Oproep", "Call feature is not supported when using Web STT engine": "Belfunctie wordt niet ondersteund bij gebruik van de Web STT engine", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Verbind met je eigen OpenAPI-compatibele externe gereedschapservers", "Connected ({{type}})": "", "Connection failed": "Connectie mislukt", + "Connection lost. Reconnecting...": "", "Connection successful": "Connectie succesvol", "Connection Type": "Connectie type", "Connections": "Verbindingen", @@ -525,6 +533,8 @@ "Delete All Chats": "Verwijder alle chats", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Verwijder chat", "Delete chat?": "Verwijder chat?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Kan API Key niet aanmaken.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Redeneerinspanning", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Neem stem op", "Redirecting you to Open WebUI Community": "Je wordt doorgestuurd naar OpenWebUI Community", @@ -1648,6 +1660,7 @@ "Relevance": "Relevantie", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Verwijderen", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "Begin van het kanaal", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Dit zal {{NAME}} verwijderen en al zijn inhoud.", "This will delete all models including custom models": "Dit zal alle modellen, ook aangepaste modellen, verwijderen", "This will delete all models including custom models and cannot be undone.": "Dit zal alle modellen, ook aangepaste modellen, verwijderen en kan niet ongedaan worden gemaakt", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Dit zal de kennisdatabase resetten en alle bestanden synchroniseren. Wilt u doorgaan?", "Thorough explanation": "Grondige uitleg", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "Ontsleutel mysteries", "Unpin": "Losmaken", + "Unpin from Sidebar": "", "Unravel secrets": "Ontrafel geheimen", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/pa-IN/translation.json b/src/lib/i18n/locales/pa-IN/translation.json index c6d006cb1a..d29adc4b55 100644 --- a/src/lib/i18n/locales/pa-IN/translation.json +++ b/src/lib/i18n/locales/pa-IN/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}} ਦੀਆਂ ਗੱਲਾਂ", "{{webUIName}} Backend Required": "{{webUIName}} ਬੈਕਐਂਡ ਲੋੜੀਂਦਾ ਹੈ", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -202,6 +207,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "", "Call feature is not supported when using Web STT engine": "", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "ਕਨੈਕਸ਼ਨ", @@ -525,6 +533,8 @@ "Delete All Chats": "ਸਾਰੀਆਂ ਚੈਟਾਂ ਨੂੰ ਮਿਟਾਓ", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "ਗੱਲਬਾਤ ਮਿਟਾਓ", "Delete chat?": "", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "API ਕੁੰਜੀ ਬਣਾਉਣ ਵਿੱਚ ਅਸਫਲ।", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "ਆਵਾਜ਼ ਰਿਕਾਰਡ ਕਰੋ", "Redirecting you to Open WebUI Community": "ਤੁਹਾਨੂੰ ਓਪਨਵੈਬਯੂਆਈ ਕਮਿਊਨਿਟੀ ਵੱਲ ਰੀਡਾਇਰੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ", @@ -1648,6 +1660,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "ਹਟਾਓ", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "ਚੈਨਲ ਦੀ ਸ਼ੁਰੂਆਤ", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "ਵਿਸਥਾਰ ਨਾਲ ਵਿਆਖਿਆ", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/pl-PL/translation.json b/src/lib/i18n/locales/pl-PL/translation.json index 1dff552813..7143c47bc7 100644 --- a/src/lib/i18n/locales/pl-PL/translation.json +++ b/src/lib/i18n/locales/pl-PL/translation.json @@ -35,8 +35,13 @@ "{{user}}'s Chats": "Czaty użytkownika {{user}}", "{{webUIName}} Backend Required": "Wymagany backend {{webUIName}}", "*Prompt node ID(s) are required for image generation": "*Do generowania obrazów wymagane jest ID węzła promptu", + "1 hour before": "", "1 Source": "1 źródło", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Kanał współpracy, do którego użytkownicy dołączają jako członkowie", "A discussion channel where access is controlled by groups and permissions": "Kanał dyskusyjny, do którego dostęp jest kontrolowany przez grupy i uprawnienia", "A new version (v{{LATEST_VERSION}}) is now available.": "Dostępna jest nowa wersja (v{{LATEST_VERSION}}).", @@ -204,6 +209,7 @@ "Ask a question": "Zadaj pytanie", "Assistant": "Asystent", "Async Embedding Processing": "Asynchroniczne przetwarzanie embeddingów", + "At time of event": "", "Attach File From Knowledge": "Dołącz plik z bazy wiedzy", "Attach Files": "", "Attach Knowledge": "Dołącz bazę wiedzy", @@ -278,6 +284,7 @@ "Bypass Web Loader": "Pomiń Web Loader", "Cache Base Model List": "Cachuj listę modeli bazowych", "Calendar": "Kalendarz", + "Calendar deleted": "", "Calendars": "", "Call": "Rozmowa", "Call feature is not supported when using Web STT engine": "Funkcja rozmowy nie jest obsługiwana przy użyciu przeglądarkowego silnika STT", @@ -415,6 +422,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Połącz z własnymi serwerami narzędzi zgodnymi z OpenAPI.", "Connected ({{type}})": "", "Connection failed": "Połączenie nieudane", + "Connection lost. Reconnecting...": "", "Connection successful": "Połączenie udane", "Connection Type": "Typ połączenia", "Connections": "Połączenia", @@ -527,6 +535,8 @@ "Delete All Chats": "Usuń wszystkie czaty", "Delete all contents inside this folder": "Usuń całą zawartość tego folderu", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Usuń czat", "Delete chat?": "Usunąć czat?", "Delete Event": "", @@ -888,6 +898,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "Nie udało się skopiować linku", "Failed to create API Key.": "Nie udało się utworzyć klucza API.", + "Failed to delete calendar": "", "Failed to delete note": "Nie udało się usunąć notatki", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "Nie udało się wyodrębnić treści z pliku: {{error}}", @@ -1627,6 +1638,7 @@ "Reasoning Effort": "Reasoning Effort", "Reasoning Tags": "Reasoning Tags", "Recently Used": "", + "Reconnected": "", "Record": "Nagraj", "Record voice": "Nagraj głos", "Redirecting you to Open WebUI Community": "Przekierowanie do społeczności Open WebUI", @@ -1650,6 +1662,7 @@ "Relevance": "Trafność", "Relevance Threshold": "Próg trafności", "Remember Dismissal": "Zapamiętaj odrzucenie", + "Reminder": "", "Remove": "Usuń", "Remove {{MODELID}} from list.": "Usuń {{MODELID}} z listy.", "Remove action": "", @@ -1898,7 +1911,13 @@ "Start a new conversation": "Rozpocznij nową rozmowę", "Start of the channel": "Początek kanału", "Start Tag": "Tag startowy", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_few": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "Status", "Status cleared successfully": "Status wyczyszczony pomyślnie", @@ -2011,6 +2030,7 @@ "This will delete {{NAME}} and all its contents.": "To usunie {{NAME}} i całą zawartość.", "This will delete all models including custom models": "To usunie wszystkie modele (w tym własne).", "This will delete all models including custom models and cannot be undone.": "To usunie wszystkie modele i jest nieodwracalne.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "To zresetuje bazę wiedzy i zsynchronizuje pliki. Kontynuować?", "Thorough explanation": "Dokładne wyjaśnienie", "Thought": "", @@ -2097,6 +2117,7 @@ "Unloads {{FROM_NOW}}": "Odładowuje za {{FROM_NOW}}", "Unlock mysteries": "Odkrywaj tajemnice", "Unpin": "Odepnij", + "Unpin from Sidebar": "", "Unravel secrets": "Rozwiązuj zagadki", "Unshare Chat": "", "Unsupported file type.": "Nieobsługiwany typ pliku.", diff --git a/src/lib/i18n/locales/pt-BR/translation.json b/src/lib/i18n/locales/pt-BR/translation.json index 0954c0a91a..bde1024811 100644 --- a/src/lib/i18n/locales/pt-BR/translation.json +++ b/src/lib/i18n/locales/pt-BR/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "Chats de {{user}}", "{{webUIName}} Backend Required": "Backend {{webUIName}} necessário", "*Prompt node ID(s) are required for image generation": "*Prompt node ID(s) são obrigatórios para gerar imagens", + "1 hour before": "", "1 Source": "1 Origem", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "1m atrás", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Um canal de colaboração onde as pessoas se juntam como membros.", "A discussion channel where access is controlled by groups and permissions": "Um canal de discussão onde o acesso é controlado por grupos e permissões.", "A new version (v{{LATEST_VERSION}}) is now available.": "Uma nova versão (v{{LATEST_VERSION}}) está disponível.", @@ -203,6 +208,7 @@ "Ask a question": "Faça uma pergunta", "Assistant": "Assistente", "Async Embedding Processing": "Processamento de Embedding assíncrono", + "At time of event": "", "Attach File From Knowledge": "Anexar arquivo da base de conhecimento", "Attach Files": "Anexar arquivos", "Attach Knowledge": "Anexar Base de Conhecimento", @@ -277,6 +283,7 @@ "Bypass Web Loader": "Ignorar carregador da Web", "Cache Base Model List": "Lista de modelos base de cache", "Calendar": "Calendário", + "Calendar deleted": "", "Calendars": "", "Call": "Chamada", "Call feature is not supported when using Web STT engine": "O recurso de chamada não é suportado ao usar o mecanismo Web STT", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Conecte-se aos seus próprios servidores de ferramentas externas compatíveis com OpenAPI.", "Connected ({{type}})": "Conectado ({{type}})", "Connection failed": "Falha na conexão", + "Connection lost. Reconnecting...": "", "Connection successful": "Conexão bem-sucedida", "Connection Type": "Tipo de conexão", "Connections": "Conexões", @@ -526,6 +534,8 @@ "Delete All Chats": "Excluir Todos os Chats", "Delete all contents inside this folder": "Apague todo o conteúdo desta pasta.", "Delete automation?": "Excluir automação?", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Excluir Chat", "Delete chat?": "Excluir chat?", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "Falha ao conectar ao servidor de terminal {{URL}}", "Failed to copy link": "Falha ao copiar o link", "Failed to create API Key.": "Falha ao criar a Chave API.", + "Failed to delete calendar": "", "Failed to delete note": "Falha ao excluir a nota", "Failed to download image": "Falha ao baixar a imagem", "Failed to extract content from the file: {{error}}": "Falha ao extrair conteúdo do arquivo: {{error}}", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "Esforço de raciocínio", "Reasoning Tags": "Tags de raciocínio", "Recently Used": "Usado recentemente", + "Reconnected": "", "Record": "Gravar", "Record voice": "Gravar voz", "Redirecting you to Open WebUI Community": "Redirecionando você para a Comunidade OpenWebUI", @@ -1649,6 +1661,7 @@ "Relevance": "Relevância", "Relevance Threshold": "Limiar de Relevância", "Remember Dismissal": "Lembrar da dispensa", + "Reminder": "", "Remove": "Remover", "Remove {{MODELID}} from list.": "Remover {{MODELID}} da lista.", "Remove action": "Remover ação", @@ -1896,7 +1909,12 @@ "Start a new conversation": "Iniciar uma nova conversa", "Start of the channel": "Início do canal", "Start Tag": "Tag inicial", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Iniciando kernel...", + "Starting now": "", "State": "Estado", "Status": "Status", "Status cleared successfully": "Status liberado com sucesso", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "Esta ação excluirá {{NAME}} e todos seus conteúdos.", "This will delete all models including custom models": "Isto vai excluir todos os modelos, incluindo personalizados", "This will delete all models including custom models and cannot be undone.": "Isto vai excluir todos os modelos, incluindo personalizados e não pode ser desfeito.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Esta ação resetará a base de conhecimento e sincronizará todos os arquivos. Deseja continuar?", "Thorough explanation": "Explicação detalhada", "Thought": "Pensamento", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "Descarrega {{FROM_NOW}}", "Unlock mysteries": "Desvendar mistérios", "Unpin": "Desfixar", + "Unpin from Sidebar": "", "Unravel secrets": "Desvendar segredos", "Unshare Chat": "Cancelar compartilhamento do chat", "Unsupported file type.": "Tipo de arquivo não suportado.", diff --git a/src/lib/i18n/locales/pt-PT/translation.json b/src/lib/i18n/locales/pt-PT/translation.json index c8f23d1dea..1b9c2e7e48 100644 --- a/src/lib/i18n/locales/pt-PT/translation.json +++ b/src/lib/i18n/locales/pt-PT/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "Chats de {{user}}", "{{webUIName}} Backend Required": "Backend {{webUIName}} Necessário", "*Prompt node ID(s) are required for image generation": "*ID(s) do nó de prompt são necessários para a geração de imagem", + "1 hour before": "", "1 Source": "Uma Fonte", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "há 1 minuto", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Um canal de colaboração onde as pessoas entram como membros", "A discussion channel where access is controlled by groups and permissions": "Um canal de discussão onde o acesso é controlado por grupos e permissões", "A new version (v{{LATEST_VERSION}}) is now available.": "Uma nova versão (v{{LATEST_VERSION}}) está agora disponível.", @@ -203,6 +208,7 @@ "Ask a question": "Fazer uma pergunta", "Assistant": "Assistente", "Async Embedding Processing": "Incorporação de Processamento Assíncrono", + "At time of event": "", "Attach File From Knowledge": "Anexar Ficheiro do Conhecimento", "Attach Files": "", "Attach Knowledge": "Anexar Conhecimento", @@ -277,6 +283,7 @@ "Bypass Web Loader": "Ignorar Carregador Web", "Cache Base Model List": "Cache da Lista de Modelos Base", "Calendar": "Calendário", + "Calendar deleted": "", "Calendars": "", "Call": "Chamar", "Call feature is not supported when using Web STT engine": "A funcionalide de Chamar não é suportada quando usa um motor Web STT", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Ligar ao seu próprio servidor de ferramentas externo compatível com a OpenAI.", "Connected ({{type}})": "", "Connection failed": "Ligação falhou", + "Connection lost. Reconnecting...": "", "Connection successful": "Ligação bem sucedida", "Connection Type": "Tipo de ligação", "Connections": "Ligações", @@ -526,6 +534,8 @@ "Delete All Chats": "Apagar todas as conversas", "Delete all contents inside this folder": "Apagar todo o conteúdo dentro desta pasta", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Apagar Conversa", "Delete chat?": "Apagar conversa?", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "Falha na ligação ao terminal de servidores {{URL}}", "Failed to copy link": "Falha ao copiar a hiperligação", "Failed to create API Key.": "Falha ao criar a Chave da API.", + "Failed to delete calendar": "", "Failed to delete note": "Falha ao apagar a nota", "Failed to download image": "Falha ao transferir a imagem", "Failed to extract content from the file: {{error}}": "Falha ao extrair conteúdo do ficheiro: {{error}}", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "Esforço de Raciocínio", "Reasoning Tags": "Etiquetas de Raciocínio", "Recently Used": "", + "Reconnected": "", "Record": "Gravar", "Record voice": "Gravar voz", "Redirecting you to Open WebUI Community": "Redirecionando-o para a Comunidade OpenWebUI", @@ -1649,6 +1661,7 @@ "Relevance": "Relevância", "Relevance Threshold": "Limite de Relevância", "Remember Dismissal": "Lembrar Descartar", + "Reminder": "", "Remove": "Remover", "Remove {{MODELID}} from list.": "Remover {{MODELID}} da lista.", "Remove action": "Remover ação", @@ -1896,7 +1909,12 @@ "Start a new conversation": "Iniciar uma nova conversa", "Start of the channel": "Início do canal", "Start Tag": "Início da Tag", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Iniciando kernel...", + "Starting now": "", "State": "", "Status": "Estado", "Status cleared successfully": "Estado limpo com sucesso", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "Isto irá excluir {{NAME}} e todo o seu conteúdo.", "This will delete all models including custom models": "Isto irá excluir todos os modelos, incluindo os modelos personalizados", "This will delete all models including custom models and cannot be undone.": "Isto irá excluir todos os modelos, incluindo os modelos personalizados, e não pode ser desfeito.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Isto irá redefinir a base de conhecimento e sincronizar todos os arquivos. Deseja continuar?", "Thorough explanation": "Explicação Minuciosa", "Thought": "", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "Descarreva {{FROM_NOW}}", "Unlock mysteries": "Desbloquear Mistérios", "Unpin": "Desafixar", + "Unpin from Sidebar": "", "Unravel secrets": "Desvendar segredos", "Unshare Chat": "Parar partilha de conversa", "Unsupported file type.": "Tipo de ficheiro não suportado", diff --git a/src/lib/i18n/locales/ro-RO/translation.json b/src/lib/i18n/locales/ro-RO/translation.json index 0215a6eacc..3028840716 100644 --- a/src/lib/i18n/locales/ro-RO/translation.json +++ b/src/lib/i18n/locales/ro-RO/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "Conversațiile lui {{user}}", "{{webUIName}} Backend Required": "Este necesar backend-ul {{webUIName}}", "*Prompt node ID(s) are required for image generation": "*Sunt necesare ID-urile nodurilor de solicitare pentru generarea imaginii*", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "O nouă versiune (v{{LATEST_VERSION}}) este acum disponibilă.", @@ -203,6 +208,7 @@ "Ask a question": "Pune o întrebare", "Assistant": "Asistent", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -277,6 +283,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "Apel", "Call feature is not supported when using Web STT engine": "Funcția de apel nu este suportată când se utilizează motorul Web STT", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "Conexiune eșuată", + "Connection lost. Reconnecting...": "", "Connection successful": "Conexiune reușită", "Connection Type": "Tip conexiune", "Connections": "Conexiuni", @@ -526,6 +534,8 @@ "Delete All Chats": "Șterge Toate Conversațiile", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Șterge Conversația", "Delete chat?": "Șterge conversația?", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Crearea cheii API a eșuat.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Înregistrează vocea", "Redirecting you to Open WebUI Community": "Vă redirecționăm către Comunitatea OpenWebUI", @@ -1649,6 +1661,7 @@ "Relevance": "Relevanță", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Înlătură", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1896,7 +1909,12 @@ "Start a new conversation": "", "Start of the channel": "Începutul canalului", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_few": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "Acest lucru va șterge {{NAME}} și toate conținuturile sale.", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Aceasta va reseta baza de cunoștințe și va sincroniza toate fișierele. Doriți să continuați?", "Thorough explanation": "Explicație detaliată", "Thought": "", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "Anulează Fixarea", + "Unpin from Sidebar": "", "Unravel secrets": "Dezvăluie secretele", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/ru-RU/translation.json b/src/lib/i18n/locales/ru-RU/translation.json index 15663aecc0..954d8f7f4e 100644 --- a/src/lib/i18n/locales/ru-RU/translation.json +++ b/src/lib/i18n/locales/ru-RU/translation.json @@ -35,8 +35,13 @@ "{{user}}'s Chats": "Чаты {{user}}'а", "{{webUIName}} Backend Required": "Необходимо подключение к серверу {{webUIName}}", "*Prompt node ID(s) are required for image generation": "ID узлов промптов обязательны для генерации изображения", + "1 hour before": "", "1 Source": "1 Источник", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "1 мин назад", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "Канал для совместной работы с присоединением участников", "A discussion channel where access is controlled by groups and permissions": "Обсуждение канала, где доступ контролируется группами и разрешениями", "A new version (v{{LATEST_VERSION}}) is now available.": "Новая версия (v{{LATEST_VERSION}}) теперь доступна.", @@ -204,6 +209,7 @@ "Ask a question": "Задать вопрос", "Assistant": "Ассистент", "Async Embedding Processing": "Асинхронная обработка эмбеддингов", + "At time of event": "", "Attach File From Knowledge": "Прикрепить файл из знаний", "Attach Files": "", "Attach Knowledge": "Прикрепить знания", @@ -278,6 +284,7 @@ "Bypass Web Loader": "Обход веб-загрузчика", "Cache Base Model List": "Кэшировать список базовых моделей", "Calendar": "Календарь", + "Calendar deleted": "", "Calendars": "", "Call": "Вызов", "Call feature is not supported when using Web STT engine": "Функция вызова не поддерживается при использовании Web STT (распознавание речи) движка", @@ -415,6 +422,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Подключитесь к вашим собственным внешним инструментальным серверам, совместимым с OpenAPI.", "Connected ({{type}})": "Подключено ({{type}})", "Connection failed": "Подключение не удалось", + "Connection lost. Reconnecting...": "", "Connection successful": "Успешное подключение", "Connection Type": "Тип подключения", "Connections": "Подключения", @@ -527,6 +535,8 @@ "Delete All Chats": "Удалить ВСЕ Чаты", "Delete all contents inside this folder": "Удалить все содержимое внутри этой папки", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Удалить Чат", "Delete chat?": "Удалить чат?", "Delete Event": "", @@ -888,6 +898,7 @@ "Failed to connect to {{URL}} terminal server": "Не удалось подключиться к серверу терминала {{URL}}", "Failed to copy link": "Не удалось скопировать ссылку", "Failed to create API Key.": "Не удалось создать ключ API.", + "Failed to delete calendar": "", "Failed to delete note": "Не удалось удалить заметку", "Failed to download image": "Не удалось загрузить изображение", "Failed to extract content from the file: {{error}}": "Не удалось извлечь содержимое из файла: {{error}}", @@ -1627,6 +1638,7 @@ "Reasoning Effort": "Усилия для рассуждения", "Reasoning Tags": "Теги рассуждения", "Recently Used": "", + "Reconnected": "", "Record": "Запись", "Record voice": "Записать голос", "Redirecting you to Open WebUI Community": "Перенаправляем вас в сообщество OpenWebUI", @@ -1650,6 +1662,7 @@ "Relevance": "Релевантность", "Relevance Threshold": "Порог релевантности", "Remember Dismissal": "Запомнить отклонение", + "Reminder": "", "Remove": "Удалить", "Remove {{MODELID}} from list.": "Удалить {{MODELID}} из списка.", "Remove action": "Удалить действие", @@ -1898,7 +1911,13 @@ "Start a new conversation": "Начать новый разговор", "Start of the channel": "Начало канала", "Start Tag": "Начальный тег", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_few": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Запуск ядра...", + "Starting now": "", "State": "", "Status": "Статус", "Status cleared successfully": "Статус успешно очищен", @@ -2011,6 +2030,7 @@ "This will delete {{NAME}} and all its contents.": "При этом будет удален {{NAME}} и все его содержимое.", "This will delete all models including custom models": "Это приведет к удалению всех моделей, включая пользовательские модели.", "This will delete all models including custom models and cannot be undone.": "При этом будут удалены все модели, включая пользовательские, и это действие нельзя будет отменить.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Это сбросит базу знаний и синхронизирует все файлы. Хотите продолжить?", "Thorough explanation": "Подробное объяснение", "Thought": "Рассуждение", @@ -2097,6 +2117,7 @@ "Unloads {{FROM_NOW}}": "Выгрузка из памяти {{FROM_NOW}}", "Unlock mysteries": "Разблокируйте тайны", "Unpin": "Открепить", + "Unpin from Sidebar": "", "Unravel secrets": "Разгадать секреты", "Unshare Chat": "Отменить публикацию чата", "Unsupported file type.": "Неподдерживаемый тип файла.", diff --git a/src/lib/i18n/locales/sk-SK/translation.json b/src/lib/i18n/locales/sk-SK/translation.json index 74d16d847c..0ecf193014 100644 --- a/src/lib/i18n/locales/sk-SK/translation.json +++ b/src/lib/i18n/locales/sk-SK/translation.json @@ -35,8 +35,13 @@ "{{user}}'s Chats": "{{user}}'s konverzácie", "{{webUIName}} Backend Required": "Vyžaduje sa {{webUIName}} Backend", "*Prompt node ID(s) are required for image generation": "*Sú potrebné IDs pre prompt node na generovanie obrázkov", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Nová verzia (v{{LATEST_VERSION}}) je teraz k dispozícii.", @@ -204,6 +209,7 @@ "Ask a question": "Opýtajte sa otázku", "Assistant": "Asistent", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "Pripojiť znalosti", @@ -278,6 +284,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "Volanie", "Call feature is not supported when using Web STT engine": "Funkcia volania nie je podporovaná pri použití Web STT engine.", @@ -415,6 +422,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "Pripojenia", @@ -527,6 +535,8 @@ "Delete All Chats": "Odstrániť všetky konverzácie", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Odstrániť chat", "Delete chat?": "Odstrániť konverzáciu?", "Delete Event": "", @@ -888,6 +898,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Nepodarilo sa vytvoriť API kľúč.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1627,6 +1638,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Nahrať hlas", "Redirecting you to Open WebUI Community": "Presmerovanie na komunitu OpenWebUI", @@ -1650,6 +1662,7 @@ "Relevance": "Relevancia", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Odstrániť", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1898,7 +1911,13 @@ "Start a new conversation": "", "Start of the channel": "Začiatok kanála", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_few": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2011,6 +2030,7 @@ "This will delete {{NAME}} and all its contents.": "Týmto dôjde k odstráneniu {{NAME}} a všetkých jeho obsahov.", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Toto obnoví znalostnú databázu a synchronizuje všetky súbory. Prajete si pokračovať?", "Thorough explanation": "Obsiahle vysvetlenie", "Thought": "", @@ -2097,6 +2117,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "Odopnúť", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/sr-RS/translation.json b/src/lib/i18n/locales/sr-RS/translation.json index fd5e72e1fb..647eb187b5 100644 --- a/src/lib/i18n/locales/sr-RS/translation.json +++ b/src/lib/i18n/locales/sr-RS/translation.json @@ -34,8 +34,13 @@ "{{user}}'s Chats": "Ћаскања корисника {{user}}", "{{webUIName}} Backend Required": "Захтева се {{webUIName}} позадинац", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -203,6 +208,7 @@ "Ask a question": "Постави питање", "Assistant": "Помоћник", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -277,6 +283,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "Позив", "Call feature is not supported when using Web STT engine": "", @@ -414,6 +421,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "Везе", @@ -526,6 +534,8 @@ "Delete All Chats": "Обриши сва ћаскања", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Обриши ћаскање", "Delete chat?": "Обрисати ћаскање?", "Delete Event": "", @@ -887,6 +897,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Неуспешно стварање API кључа.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1626,6 +1637,7 @@ "Reasoning Effort": "Јачина размишљања", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Сними глас", "Redirecting you to Open WebUI Community": "Преусмеравање на OpenWebUI заједницу", @@ -1649,6 +1661,7 @@ "Relevance": "Примењивост", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Уклони", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1896,7 +1909,12 @@ "Start a new conversation": "", "Start of the channel": "Почетак канала", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_few": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2009,6 +2027,7 @@ "This will delete {{NAME}} and all its contents.": "Ово ће обрисати {{NAME}} и сав садржај унутар.", "This will delete all models including custom models": "Ово ће обрисати све моделе укључујући прилагођене моделе", "This will delete all models including custom models and cannot be undone.": "Ово ће обрисати све моделе укључујући прилагођене моделе и не може се опозвати.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Ово ће обрисати базу знања и ускладити све датотеке. Да ли желите наставити?", "Thorough explanation": "Детаљно објашњење", "Thought": "", @@ -2095,6 +2114,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "Реши мистерије", "Unpin": "Откачи", + "Unpin from Sidebar": "", "Unravel secrets": "Разоткриј тајне", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/sv-SE/translation.json b/src/lib/i18n/locales/sv-SE/translation.json index d75b41b928..9915d73f25 100644 --- a/src/lib/i18n/locales/sv-SE/translation.json +++ b/src/lib/i18n/locales/sv-SE/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}}s Chattar", "{{webUIName}} Backend Required": "{{webUIName}} Backend krävs", "*Prompt node ID(s) are required for image generation": "*Prompt node ID(s) krävs för bildgenerering", + "1 hour before": "", "1 Source": "1 källa", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "En ny version (v{{LATEST_VERSION}}) är nu tillgänglig.", @@ -202,6 +207,7 @@ "Ask a question": "Ställ en fråga", "Assistant": "Assistent", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "Bifoga kunskap", @@ -276,6 +282,7 @@ "Bypass Web Loader": "Kringgå webbläsare", "Cache Base Model List": "", "Calendar": "Kalender", + "Calendar deleted": "", "Calendars": "", "Call": "Samtal", "Call feature is not supported when using Web STT engine": "Samtalsfunktionen är inte kompatibel med Web Tal-till-text motor", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Anslut till dina egna OpenAPI-kompatibla externa verktygsservrar.", "Connected ({{type}})": "", "Connection failed": "Anslutning misslyckades", + "Connection lost. Reconnecting...": "", "Connection successful": "Anslutning lyckades", "Connection Type": "Anslutningstyp", "Connections": "Anslutningar", @@ -525,6 +533,8 @@ "Delete All Chats": "Ta bort alla chattar", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Radera chatt", "Delete chat?": "Radera chatt?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "Misslyckades med att kopiera länk", "Failed to create API Key.": "Misslyckades med att skapa API-nyckel.", + "Failed to delete calendar": "", "Failed to delete note": "Misslyckades med att ta bort anteckning", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Resonemangsinsats", "Reasoning Tags": "Resonemangs-taggar (tags)", "Recently Used": "", + "Reconnected": "", "Record": "Spela in", "Record voice": "Spela in röst", "Redirecting you to Open WebUI Community": "Omdirigerar dig till OpenWebUI Community", @@ -1648,6 +1660,7 @@ "Relevance": "Relevans", "Relevance Threshold": "Relevanströskel", "Remember Dismissal": "Kom ihåg avvisning", + "Reminder": "", "Remove": "Ta bort", "Remove {{MODELID}} from list.": "Ta bort {{MODELID}} från listan.", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "Starta en ny konversation", "Start of the channel": "Början av kanalen", "Start Tag": "Starta en tagg", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Detta kommer att radera {{NAME}} och allt dess innehåll.", "This will delete all models including custom models": "Detta kommer att radera alla modeller inklusive anpassade modeller", "This will delete all models including custom models and cannot be undone.": "Detta kommer att radera alla modeller inklusive anpassade modeller och kan inte ångras.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Detta kommer att återställa kunskapsbasen och synkronisera alla filer. Vill du fortsätta?", "Thorough explanation": "Djupare förklaring", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "Avlastar {{FROM_NOW}}", "Unlock mysteries": "Lås upp mysterier", "Unpin": "Ta bort fästning", + "Unpin from Sidebar": "", "Unravel secrets": "Avslöja hemligheter", "Unshare Chat": "", "Unsupported file type.": "Filtypen stöds inte.", diff --git a/src/lib/i18n/locales/ta-IN/translation.json b/src/lib/i18n/locales/ta-IN/translation.json index 8e5af4f6e2..646aec1471 100644 --- a/src/lib/i18n/locales/ta-IN/translation.json +++ b/src/lib/i18n/locales/ta-IN/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}} இன் அரட்டைகள்", "{{webUIName}} Backend Required": "{{webUIName}} பின்தளம் தேவை", "*Prompt node ID(s) are required for image generation": "*பட உருவாக்கத்திற்கு உடனடி முனை ID(கள்) தேவை", + "1 hour before": "", "1 Source": "1 ஆதாரம்", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "1 நிமிடம் முன்", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "மக்கள் உறுப்பினர்களாக சேரும் ஒத்துழைப்பு சேனல்", "A discussion channel where access is controlled by groups and permissions": "குழுக்கள் மற்றும் அனுமதிகளால் அணுகல் கட்டுப்படுத்தப்படும் விவாத சேனல்", "A new version (v{{LATEST_VERSION}}) is now available.": "புதிய பதிப்பு (v{{LATEST_VERSION}}) இப்போது கிடைக்கிறது.", @@ -202,6 +207,7 @@ "Ask a question": "ஒரு கேள்வி கேளுங்கள்", "Assistant": "உதவியாளர்", "Async Embedding Processing": "ஒத்திசைவு உட்பொதித்தல் செயலாக்கம்", + "At time of event": "", "Attach File From Knowledge": "அறிவிலிருந்து கோப்பை இணைக்கவும்", "Attach Files": "கோப்புகளை இணைக்கவும்", "Attach Knowledge": "அறிவை இணைக்கவும்", @@ -276,6 +282,7 @@ "Bypass Web Loader": "பைபாஸ் இணைய ஏற்றி", "Cache Base Model List": "கேச் அடிப்படை மாதிரி பட்டியல்", "Calendar": "நாட்காட்டி", + "Calendar deleted": "", "Calendars": "", "Call": "அழைக்கவும்", "Call feature is not supported when using Web STT engine": "Web STT இன்ஜினைப் பயன்படுத்தும் போது அழைப்பு அம்சம் ஆதரிக்கப்படாது", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "உங்கள் சொந்த OpenAPI இணக்கமான வெளிப்புற கருவி சேவையகங்களுடன் இணைக்கவும்.", "Connected ({{type}})": "இணைக்கப்பட்டது ({{type}})", "Connection failed": "இணைப்பு தோல்வியடைந்தது", + "Connection lost. Reconnecting...": "", "Connection successful": "இணைப்பு வெற்றிகரமாக உள்ளது", "Connection Type": "இணைப்பு வகை", "Connections": "இணைப்புகள்", @@ -525,6 +533,8 @@ "Delete All Chats": "அனைத்து அரட்டைகளையும் நீக்கு", "Delete all contents inside this folder": "இந்தக் கோப்புறையில் உள்ள அனைத்து உள்ளடக்கங்களையும் நீக்கவும்", "Delete automation?": "தானியக்கத்தை நீக்கவா?", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "அரட்டையை நீக்கு", "Delete chat?": "அரட்டையை நீக்கவா?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "{{URL}} டெர்மினல் சர்வருடன் இணைக்க முடியவில்லை", "Failed to copy link": "இணைப்பை நகலெடுக்க முடியவில்லை", "Failed to create API Key.": "API விசையை உருவாக்குவதில் தோல்வி.", + "Failed to delete calendar": "", "Failed to delete note": "குறிப்பை நீக்க முடியவில்லை", "Failed to download image": "படத்தைப் பதிவிறக்க முடியவில்லை", "Failed to extract content from the file: {{error}}": "கோப்பிலிருந்து உள்ளடக்கத்தைப் பிரித்தெடுக்க முடியவில்லை: {{error}}", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "பகுத்தறிவு முயற்சி", "Reasoning Tags": "பகுத்தறிவு குறிச்சொற்கள்", "Recently Used": "சமீபத்தில் பயன்படுத்தப்பட்டது", + "Reconnected": "", "Record": "பதிவு", "Record voice": "குரல் பதிவு", "Redirecting you to Open WebUI Community": "உங்களை Open WebUI சமூகத்திற்கு திருப்பி விடுகிறோம்", @@ -1648,6 +1660,7 @@ "Relevance": "சம்பந்தம்", "Relevance Threshold": "சம்பந்தமான வரம்பு", "Remember Dismissal": "பணிநீக்கம் என்பதை நினைவில் கொள்க", + "Reminder": "", "Remove": "அகற்று", "Remove {{MODELID}} from list.": "பட்டியலில் இருந்து {{MODELID}} ஐ அகற்று.", "Remove action": "செயலை அகற்று", @@ -1894,7 +1907,11 @@ "Start a new conversation": "புதிய உரையாடலைத் தொடங்கவும்", "Start of the channel": "சேனலின் ஆரம்பம்", "Start Tag": "தொடக்க குறிச்சொல்", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "கர்னலைத் தொடங்குகிறது...", + "Starting now": "", "State": "நிலை", "Status": "நிலை", "Status cleared successfully": "நிலை வெற்றிகரமாக அழிக்கப்பட்டது", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "இது {{NAME}} மற்றும் அதன் அனைத்து உள்ளடக்கங்களையும் நீக்கும்.", "This will delete all models including custom models": "இது தனிப்பயன் மாதிரிகள் உட்பட அனைத்து மாடல்களையும் நீக்கும்", "This will delete all models including custom models and cannot be undone.": "இது தனிப்பயன் மாதிரிகள் உட்பட அனைத்து மாடல்களையும் நீக்கிவிடும், மேலும் செயல்தவிர்க்க முடியாது.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "இது அறிவுத் தளத்தை மீட்டமைத்து அனைத்து கோப்புகளையும் ஒத்திசைக்கும். நீங்கள் தொடர விரும்புகிறீர்களா?", "Thorough explanation": "விரிவான விளக்கம்", "Thought": "சிந்தனை", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "{{FROM_NOW}} இறக்குகிறது", "Unlock mysteries": "மர்மங்களைத் திறக்கவும்", "Unpin": "அன்பின்", + "Unpin from Sidebar": "", "Unravel secrets": "இரகசியங்களை அவிழ்த்து விடுங்கள்", "Unshare Chat": "அரட்டையைப் பகிர்வதை நீக்கு", "Unsupported file type.": "ஆதரிக்கப்படாத கோப்பு வகை.", diff --git a/src/lib/i18n/locales/th-TH/translation.json b/src/lib/i18n/locales/th-TH/translation.json index 733bd77d5e..17dd64ef45 100644 --- a/src/lib/i18n/locales/th-TH/translation.json +++ b/src/lib/i18n/locales/th-TH/translation.json @@ -32,8 +32,13 @@ "{{user}}'s Chats": "การแชทของ {{user}}", "{{webUIName}} Backend Required": "ต้องใช้ Backend ของ {{webUIName}}", "*Prompt node ID(s) are required for image generation": "*ต้องระบุ ID ของ prompt node สำหรับการสร้างภาพ", + "1 hour before": "", "1 Source": "1 แหล่งที่มา", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "เวอร์ชันใหม่ (v{{LATEST_VERSION}}) พร้อมให้ใช้งานแล้ว", @@ -201,6 +206,7 @@ "Ask a question": "ถามคำถาม", "Assistant": "ผู้ช่วย", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "แนบไฟล์จากฐานความรู้", "Attach Files": "", "Attach Knowledge": "แนบฐานความรู้", @@ -275,6 +281,7 @@ "Bypass Web Loader": "ข้ามตัวโหลดเว็บไซต์", "Cache Base Model List": "แคชรายการโมเดลพื้นฐาน", "Calendar": "ปฏิทิน", + "Calendar deleted": "", "Calendars": "", "Call": "โทร", "Call feature is not supported when using Web STT engine": "ไม่รองรับฟีเจอร์การโทรเมื่อใช้เอนจิน Web STT", @@ -412,6 +419,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "เชื่อมต่อกับเซิร์ฟเวอร์เครื่องมือภายนอกของคุณที่รองรับ OpenAPI", "Connected ({{type}})": "", "Connection failed": "การเชื่อมต่อล้มเหลว", + "Connection lost. Reconnecting...": "", "Connection successful": "เชื่อมต่อสำเร็จ", "Connection Type": "ประเภทการเชื่อมต่อ", "Connections": "การเชื่อมต่อ", @@ -524,6 +532,8 @@ "Delete All Chats": "ลบการแชททั้งหมด", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "ลบแชท", "Delete chat?": "ลบแชท?", "Delete Event": "", @@ -885,6 +895,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "คัดลอกลิงก์ไม่สำเร็จ", "Failed to create API Key.": "สร้าง API Key ล้มเหลว", + "Failed to delete calendar": "", "Failed to delete note": "ลบบันทึกไม่สำเร็จ", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "ไม่สามารถดึงเนื้อหาจากไฟล์ได้: {{error}}", @@ -1624,6 +1635,7 @@ "Reasoning Effort": "ระดับการใช้เหตุผล", "Reasoning Tags": "ป้ายกำกับการให้เหตุผล", "Recently Used": "", + "Reconnected": "", "Record": "บันทึก", "Record voice": "บันทึกเสียง", "Redirecting you to Open WebUI Community": "กำลังเปลี่ยนเส้นทางคุณไปยังชุมชน Open WebUI", @@ -1647,6 +1659,7 @@ "Relevance": "ความเกี่ยวข้อง", "Relevance Threshold": "เกณฑ์ความเกี่ยวข้อง", "Remember Dismissal": "จำการปิดข้อความ", + "Reminder": "", "Remove": "ลบ", "Remove {{MODELID}} from list.": "ลบ {{MODELID}} ออกจากรายการ", "Remove action": "", @@ -1892,7 +1905,10 @@ "Start a new conversation": "เริ่มการสนทนาใหม่", "Start of the channel": "จุดเริ่มต้นของช่อง", "Start Tag": "แท็กเริ่มต้น", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2005,6 +2021,7 @@ "This will delete {{NAME}} and all its contents.": "การดำเนินการนี้จะลบ {{NAME}} และเนื้อหาทั้งหมด", "This will delete all models including custom models": "การดำเนินการนี้จะลบโมเดลทั้งหมด รวมถึงโมเดลแบบกำหนดเอง", "This will delete all models including custom models and cannot be undone.": "การดำเนินการนี้จะลบโมเดลทั้งหมดรวมถึงโมเดลที่กำหนดเอง และไม่สามารถยกเลิกได้", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "การดำเนินการนี้จะรีเซ็ตฐานความรู้และซิงค์ไฟล์ทั้งหมด คุณต้องการดำเนินการต่อหรือไม่?", "Thorough explanation": "คำอธิบายอย่างละเอียด", "Thought": "", @@ -2091,6 +2108,7 @@ "Unloads {{FROM_NOW}}": "ยกเลิกการใช้งาน {{FROM_NOW}}", "Unlock mysteries": "ไขปริศนา", "Unpin": "ยกเลิกการปักหมุด", + "Unpin from Sidebar": "", "Unravel secrets": "เปิดเผยความลับ", "Unshare Chat": "", "Unsupported file type.": "ไม่รองรับไฟล์ประเภทนี้", diff --git a/src/lib/i18n/locales/tk-TM/translation.json b/src/lib/i18n/locales/tk-TM/translation.json index 7fb04a3227..6783a0222a 100644 --- a/src/lib/i18n/locales/tk-TM/translation.json +++ b/src/lib/i18n/locales/tk-TM/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}}'iň Çatlary", "{{webUIName}} Backend Required": "{{webUIName}} Backend Zerur", "*Prompt node ID(s) are required for image generation": "", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "", @@ -202,6 +207,7 @@ "Ask a question": "", "Assistant": "", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "", "Call feature is not supported when using Web STT engine": "", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "Baglanyşyklar", @@ -525,6 +533,8 @@ "Delete All Chats": "Ähli Çatlary Öçür", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "", "Delete chat?": "", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "", "Redirecting you to Open WebUI Community": "", @@ -1648,6 +1660,7 @@ "Relevance": "", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Aýyr", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "Kanal başy", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "", "Thorough explanation": "", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/tr-TR/translation.json b/src/lib/i18n/locales/tr-TR/translation.json index 8d9e63ce85..5b31954239 100644 --- a/src/lib/i18n/locales/tr-TR/translation.json +++ b/src/lib/i18n/locales/tr-TR/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}}'ın Sohbetleri", "{{webUIName}} Backend Required": "{{webUIName}} Arka-uç Gerekli", "*Prompt node ID(s) are required for image generation": "*Görüntü oluşturma için düğüm ID'leri gereklidir", + "1 hour before": "", "1 Source": "1 Kaynak", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "1 dk önce", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "İnsanların üye olarak katıldığı bir iş birliği kanalı", "A discussion channel where access is controlled by groups and permissions": "Erişimin gruplar ve izinlerle kontrol edildiği bir tartışma kanalı", "A new version (v{{LATEST_VERSION}}) is now available.": "Yeni bir sürüm (v{{LATEST_VERSION}}) artık mevcut.", @@ -202,6 +207,7 @@ "Ask a question": "Bir soru sorun", "Assistant": "Asistan", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "Bilgi Tabanından Dosya Ekle", "Attach Files": "", "Attach Knowledge": "Bilgi Tabanı Ekle", @@ -276,6 +282,7 @@ "Bypass Web Loader": "Web Yükleyicisini Atla", "Cache Base Model List": "Temel Model Listesini Önbelleğe Al", "Calendar": "Takvim", + "Calendar deleted": "", "Calendars": "", "Call": "Arama", "Call feature is not supported when using Web STT engine": "Web STT motoru kullanılırken arama özelliği desteklenmiyor", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Kendi OpenAPI uyumlu harici araç sunucularınıza bağlanın.", "Connected ({{type}})": "", "Connection failed": "Bağlantı başarısız", + "Connection lost. Reconnecting...": "", "Connection successful": "Bağlantı başarılı", "Connection Type": "Bağlantı Tipi", "Connections": "Bağlantılar", @@ -525,6 +533,8 @@ "Delete All Chats": "Tüm Sohbetleri Sil", "Delete all contents inside this folder": "Bu klasördeki tüm içerikleri sil", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Sohbeti Sil", "Delete chat?": "Sohbeti sil?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "{{URL}} terminal sunucusuna bağlanılamadı", "Failed to copy link": "Bağlantı kopyalanamadı", "Failed to create API Key.": "API Anahtarı oluşturulamadı.", + "Failed to delete calendar": "", "Failed to delete note": "Not silinemedi", "Failed to download image": "Görsel indirilemedi", "Failed to extract content from the file: {{error}}": "Dosyadan içerik çıkarılamadı: {{error}}", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "Kaydet", "Record voice": "Ses kaydı yap", "Redirecting you to Open WebUI Community": "OpenWebUI Topluluğuna yönlendiriliyorsunuz", @@ -1648,6 +1660,7 @@ "Relevance": "İlgili", "Relevance Threshold": "İlgi Eşiği", "Remember Dismissal": "", + "Reminder": "", "Remove": "Kaldır", "Remove {{MODELID}} from list.": "{{MODELID}} modelini listeden kaldır.", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "Yeni bir konuşma başlat", "Start of the channel": "Kanalın başlangıcı", "Start Tag": "Başlangıç Etiketi", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "Kernel başlatılıyor...", + "Starting now": "", "State": "", "Status": "Durum", "Status cleared successfully": "Durum başarıyla temizlendi", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "{{NAME}} ve tüm içeriği silinecek.", "This will delete all models including custom models": "Bu, özel modeller dahil olmak üzere tüm modelleri silecek", "This will delete all models including custom models and cannot be undone.": "Bu, özel modeller dahil olmak üzere tüm modelleri silecek ve geri alınamaz.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Bu, bilgi tabanını sıfırlayacak ve tüm dosyaları senkronize edecek. Devam etmek istiyor musunuz?", "Thorough explanation": "Kapsamlı açıklama", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "{{FROM_NOW}} sonra modeli bellekten boşaltır", "Unlock mysteries": "", "Unpin": "Sabitlemeyi Kaldır", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/ug-CN/translation.json b/src/lib/i18n/locales/ug-CN/translation.json index ea8c92b507..ba0727be45 100644 --- a/src/lib/i18n/locales/ug-CN/translation.json +++ b/src/lib/i18n/locales/ug-CN/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}} نىڭ سۆھبەتلىرى", "{{webUIName}} Backend Required": "{{webUIName}} ئارقا سۇپا زۆرۈر", "*Prompt node ID(s) are required for image generation": "رەسىم ھاسىل قىلىش ئۈچۈن تۈرتكە نۇسخا ئۇچۇر ID(لىرى) زۆرۈر", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "يېڭى نەشرى (v{{LATEST_VERSION}}) مەۋجۇت.", @@ -202,6 +207,7 @@ "Ask a question": "سؤئال سوراڭ", "Assistant": "ياردەمچى", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "تور يۈكلىگۈچتىن ئۆتۈپ كېتىش", "Cache Base Model List": "", "Calendar": "كالىندار", + "Calendar deleted": "", "Calendars": "", "Call": "چاقىرىش", "Call feature is not supported when using Web STT engine": "تور STT ماتورى ئىشلىتىلگەندە چاقىرىش ئىقتىدارى قوللىنىلمايدۇ", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "OpenAPI ماس كېلىدىغان سىرتقى قورال مۇلازىمېتىرلىرىغا باغلىنىڭ.", "Connected ({{type}})": "", "Connection failed": "ئۇلىنىش مەغلۇپ بولدى", + "Connection lost. Reconnecting...": "", "Connection successful": "ئۇلىنىش مۇۋەپپەقىيەتلىك", "Connection Type": "ئۇلىنىش تىپى", "Connections": "ئۇلىنىشلەر", @@ -525,6 +533,8 @@ "Delete All Chats": "بارلىق سۆھبەتلەرنى ئۆچۈرۈش", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "سۆھبەت ئۆچۈرۈش", "Delete chat?": "سۆھبەت ئۆچۈرەمسىز؟", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "ئۇلانما كۆچۈرۈش مەغلۇپ بولدى", "Failed to create API Key.": "API ئاچقۇچى قۇرۇش مەغلۇپ بولدى.", + "Failed to delete calendar": "", "Failed to delete note": "خاتىرە ئۆچۈرۈش مەغلۇپ بولدى", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "چۈشەندۈرۈش كۈچى", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "خاتىرىلەش", "Record voice": "ئاۋاز خاتىرىلەش", "Redirecting you to Open WebUI Community": "Open WebUI جەمئىيىتىگە يوللاندى", @@ -1648,6 +1660,7 @@ "Relevance": "مۇناسىۋەتلىكلىك", "Relevance Threshold": "مۇناسىۋەتلىكلىك چەك قىممىتى", "Remember Dismissal": "", + "Reminder": "", "Remove": "چىقىرىۋېتىش", "Remove {{MODELID}} from list.": "تىزىمدىن {{MODELID}} چىقىرىۋېتىش.", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "قانالنىڭ باشلانغىنى", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "{{NAME}} ۋە بارلىق مەزمۇنى ئۆچۈرۈلىدۇ.", "This will delete all models including custom models": "بۇ بارلىق مودېللارنى ئۆچۈرۈدۇ (ئۆزلۈك مودېللارنىمۇ ئۆز ئىچىگە ئالىدۇ)", "This will delete all models including custom models and cannot be undone.": "بۇ بارلىق مودېللارنى ئۆچۈرۈدۇ (ئۆزلۈك مودېللارنىمۇ ئۆز ئىچىگە ئالىدۇ) ۋە ئەسلىگە كەلتۈرگىلى بولمايدۇ.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "بىلىم ئاساسى قايتا تەڭشىلىپ بارلىق ھۆججەتلەر ماس-قەدەملىنىدۇ. داۋاملاشامسىز؟", "Thorough explanation": "تەپسىلىي چۈشەندۈرۈش", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "{{FROM_NOW}} چىقىرىلىدۇ", "Unlock mysteries": "سىرلارنى ئاچ", "Unpin": "مۇقىملانمىغان قىلىش", + "Unpin from Sidebar": "", "Unravel secrets": "سىرنى ئاچ", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/uk-UA/translation.json b/src/lib/i18n/locales/uk-UA/translation.json index 5dde246b1f..46de023a39 100644 --- a/src/lib/i18n/locales/uk-UA/translation.json +++ b/src/lib/i18n/locales/uk-UA/translation.json @@ -35,8 +35,13 @@ "{{user}}'s Chats": "Чати {{user}}а", "{{webUIName}} Backend Required": "Необхідно підключення бекенду {{webUIName}}", "*Prompt node ID(s) are required for image generation": "*Для генерації зображення потрібно вказати ідентифікатор(и) вузла(ів)", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Нова версія (v{{LATEST_VERSION}}) зараз доступна.", @@ -204,6 +209,7 @@ "Ask a question": "Задати питання", "Assistant": "Асистент", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -278,6 +284,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "Календар", + "Calendar deleted": "", "Calendars": "", "Call": "Виклик", "Call feature is not supported when using Web STT engine": "Функція виклику не підтримується при використанні Web STT (розпізнавання мовлення) рушія", @@ -415,6 +422,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Підключіться до своїх власних зовнішніх серверів інструментів, сумісних з OpenAPI.", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "З'єднання", @@ -527,6 +535,8 @@ "Delete All Chats": "Видалити усі чати", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Видалити чат", "Delete chat?": "Видалити чат?", "Delete Event": "", @@ -888,6 +898,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Не вдалося створити API ключ.", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1627,6 +1638,7 @@ "Reasoning Effort": "Зусилля на міркування", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Записати голос", "Redirecting you to Open WebUI Community": "Перенаправляємо вас до спільноти OpenWebUI", @@ -1650,6 +1662,7 @@ "Relevance": "Актуальність", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Видалити", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1898,7 +1911,13 @@ "Start a new conversation": "", "Start of the channel": "Початок каналу", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_few": "", + "Starting in {{count}} minutes_many": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2011,6 +2030,7 @@ "This will delete {{NAME}} and all its contents.": "Це видалить {{NAME}} та усі його вмісти.", "This will delete all models including custom models": "Це видалить усі моделі, включаючи користувацькі моделі", "This will delete all models including custom models and cannot be undone.": "Це видалить усі моделі, включаючи користувацькі моделі, і не може бути скасовано.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Це скине базу знань і синхронізує усі файли. Ви бажаєте продовжити?", "Thorough explanation": "Детальне пояснення", "Thought": "", @@ -2097,6 +2117,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "Розкрийте таємниці", "Unpin": "Відчепити", + "Unpin from Sidebar": "", "Unravel secrets": "Розплутуйте секрети", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/ur-PK/translation.json b/src/lib/i18n/locales/ur-PK/translation.json index bc6d4912e0..967dd471db 100644 --- a/src/lib/i18n/locales/ur-PK/translation.json +++ b/src/lib/i18n/locales/ur-PK/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{ صارف }} کی بات چیت", "{{webUIName}} Backend Required": "{{webUIName}} بیک اینڈ درکار ہے", "*Prompt node ID(s) are required for image generation": "تصویر کی تخلیق کے لیے *پرومپٹ نوڈ آئی ڈی(ز) کی ضرورت ہے", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "نیا ورژن (v{{LATEST_VERSION}}) اب دستیاب ہے", @@ -202,6 +207,7 @@ "Ask a question": "سوال پوچھیں", "Assistant": "اسسٹنٹ", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "", + "Calendar deleted": "", "Calendars": "", "Call": "کال کریں", "Call feature is not supported when using Web STT engine": "کال کی خصوصیت ویب STT انجن استعمال کرتے وقت معاونت یافتہ نہیں ہے", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "", "Connected ({{type}})": "", "Connection failed": "", + "Connection lost. Reconnecting...": "", "Connection successful": "", "Connection Type": "", "Connections": "کنکشنز", @@ -525,6 +533,8 @@ "Delete All Chats": "تمام چیٹس حذف کریں", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "چیٹ حذف کریں", "Delete chat?": "چیٹ حذف کریں؟", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "API کلید بنانے میں ناکام", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "صوت ریکارڈ کریں", "Redirecting you to Open WebUI Community": "آپ کو اوپن ویب یو آئی کمیونٹی کی طرف ری ڈائریکٹ کیا جا رہا ہے", @@ -1648,6 +1660,7 @@ "Relevance": "موزونیت", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "ہٹا دیں", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "چینل کی شروعات", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "یہ {{NAME}} اور اس کے تمام مواد کو حذف کر دے گا", "This will delete all models including custom models": "", "This will delete all models including custom models and cannot be undone.": "", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "یہ علمی بنیاد کو دوبارہ ترتیب دے گا اور تمام فائلز کو متوازن کرے گا کیا آپ جاری رکھنا چاہتے ہیں؟", "Thorough explanation": "مکمل وضاحت", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "", "Unpin": "ان پن کریں", + "Unpin from Sidebar": "", "Unravel secrets": "", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/uz-Cyrl-UZ/translation.json b/src/lib/i18n/locales/uz-Cyrl-UZ/translation.json index b4193be7b8..fe4f5b1da1 100644 --- a/src/lib/i18n/locales/uz-Cyrl-UZ/translation.json +++ b/src/lib/i18n/locales/uz-Cyrl-UZ/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}} нинг чатлари", "{{webUIName}} Backend Required": "{{webUIName}} Баcкенд талаб қилинади", "*Prompt node ID(s) are required for image generation": "*Расм яратиш учун тезкор тугун идентификаторлари талаб қилинади", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Энди янги версия (v{{LATEST_VERSION}}) мавжуд.", @@ -202,6 +207,7 @@ "Ask a question": "Савол беринг", "Assistant": "Ёрдамчи", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "Веб юклагични четлаб ўтиш", "Cache Base Model List": "", "Calendar": "Календар", + "Calendar deleted": "", "Calendars": "", "Call": "Қўнғироқ қилинг", "Call feature is not supported when using Web STT engine": "Wеб СТТ механизмидан фойдаланилганда қўнғироқ функсияси қўллаб-қувватланмайди", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Ўзингизнинг OpenAIга мос келадиган ташқи асбоблар серверларига уланинг.", "Connected ({{type}})": "", "Connection failed": "Уланиш амалга ошмади", + "Connection lost. Reconnecting...": "", "Connection successful": "Уланиш муваффақиятли", "Connection Type": "Уланиш тури", "Connections": "Уланишлар", @@ -525,6 +533,8 @@ "Delete All Chats": "Барча суҳбатларни ўчириш", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Чатни ўчириш", "Delete chat?": "Чат ўчирилсинми?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "Ҳаволани нусхалаб бўлмади", "Failed to create API Key.": "АПИ калитини яратиб бўлмади.", + "Failed to delete calendar": "", "Failed to delete note": "Қайдни ўчириб бўлмади", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Мулоҳаза юритиш ҳаракатлари", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "Ёзиб олиш", "Record voice": "Овозни ёзиб олинг", "Redirecting you to Open WebUI Community": "Сизни Опен WебУИ ҳамжамиятига йўналтирмоқда", @@ -1648,6 +1660,7 @@ "Relevance": "Мувофиқлик", "Relevance Threshold": "Мувофиқлик чегараси", "Remember Dismissal": "", + "Reminder": "", "Remove": "Ўчириш", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "Канал боши", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Бу <стронг>{{NAME}} ва <стронг>барча мазмунини ўчириб ташлайди.", "This will delete all models including custom models": "Бу барча моделларни, шу жумладан махсус моделларни ўчириб ташлайди", "This will delete all models including custom models and cannot be undone.": "Бу барча моделларни, жумладан, махсус моделларни ҳам ўчириб ташлайди ва уларни ортга қайтариб бўлмайди.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Бу билимлар базасини қайта тиклайди ва барча файлларни синхронлаштиради. Давом этишни хоҳлайсизми?", "Thorough explanation": "Тўлиқ тушунтириш", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "{{FROM_NOW}} юклайди", "Unlock mysteries": "Сирларни очинг", "Unpin": "Ечиш", + "Unpin from Sidebar": "", "Unravel secrets": "Сирларни очинг", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/uz-Latn-Uz/translation.json b/src/lib/i18n/locales/uz-Latn-Uz/translation.json index b7c12ae135..2ffada0eab 100644 --- a/src/lib/i18n/locales/uz-Latn-Uz/translation.json +++ b/src/lib/i18n/locales/uz-Latn-Uz/translation.json @@ -33,8 +33,13 @@ "{{user}}'s Chats": "{{user}} ning chatlari", "{{webUIName}} Backend Required": "{{webUIName}} Backend talab qilinadi", "*Prompt node ID(s) are required for image generation": "*Rasm yaratish uchun tezkor tugun identifikatorlari talab qilinadi", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Endi yangi versiya (v{{LATEST_VERSION}}) mavjud.", @@ -202,6 +207,7 @@ "Ask a question": "Savol bering", "Assistant": "Yordamchi", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -276,6 +282,7 @@ "Bypass Web Loader": "Veb yuklagichni chetlab o'tish", "Cache Base Model List": "", "Calendar": "Kalendar", + "Calendar deleted": "", "Calendars": "", "Call": "Qo'ng'iroq qiling", "Call feature is not supported when using Web STT engine": "Web STT mexanizmidan foydalanilganda qo'ng'iroq funksiyasi qo'llab-quvvatlanmaydi", @@ -413,6 +420,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "O'zingizning OpenAPI-ga mos keladigan tashqi asboblar serverlariga ulaning.", "Connected ({{type}})": "", "Connection failed": "Ulanish amalga oshmadi", + "Connection lost. Reconnecting...": "", "Connection successful": "Ulanish muvaffaqiyatli", "Connection Type": "Ulanish turi", "Connections": "Ulanishlar", @@ -525,6 +533,8 @@ "Delete All Chats": "Barcha suhbatlarni o'chirish", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Chatni oʻchirish", "Delete chat?": "Chat oʻchirilsinmi?", "Delete Event": "", @@ -886,6 +896,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "Havolani nusxalab bo‘lmadi", "Failed to create API Key.": "API kalitini yaratib bo‘lmadi.", + "Failed to delete calendar": "", "Failed to delete note": "Qaydni o‘chirib bo‘lmadi", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1625,6 +1636,7 @@ "Reasoning Effort": "Mulohaza yuritish harakatlari", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "Yozib olish", "Record voice": "Ovozni yozib oling", "Redirecting you to Open WebUI Community": "Sizni Open WebUI hamjamiyatiga yoʻnaltirmoqda", @@ -1648,6 +1660,7 @@ "Relevance": "Muvofiqlik", "Relevance Threshold": "Muvofiqlik chegarasi", "Remember Dismissal": "", + "Reminder": "", "Remove": "O'chirish", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1894,7 +1907,11 @@ "Start a new conversation": "", "Start of the channel": "Kanal boshlanishi", "Start Tag": "", + "Starting in {{count}} minutes_one": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2007,6 +2024,7 @@ "This will delete {{NAME}} and all its contents.": "Bu {{NAME}} va barcha mazmunini o‘chirib tashlaydi.", "This will delete all models including custom models": "Bu barcha modellarni, shu jumladan maxsus modellarni o'chirib tashlaydi", "This will delete all models including custom models and cannot be undone.": "Bu barcha modellarni, jumladan, maxsus modellarni ham o‘chirib tashlaydi va ularni ortga qaytarib bo‘lmaydi.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Bu bilimlar bazasini qayta tiklaydi va barcha fayllarni sinxronlashtiradi. Davom etishni xohlaysizmi?", "Thorough explanation": "To'liq tushuntirish", "Thought": "", @@ -2093,6 +2111,7 @@ "Unloads {{FROM_NOW}}": "{{FROM_NOW}} yuklaydi", "Unlock mysteries": "Sirlarni oching", "Unpin": "Yechish", + "Unpin from Sidebar": "", "Unravel secrets": "Sirlarni oching", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/vi-VN/translation.json b/src/lib/i18n/locales/vi-VN/translation.json index 9296b8b57c..6810eb909a 100644 --- a/src/lib/i18n/locales/vi-VN/translation.json +++ b/src/lib/i18n/locales/vi-VN/translation.json @@ -32,8 +32,13 @@ "{{user}}'s Chats": "Các cuộc trò chuyện của {{user}}", "{{webUIName}} Backend Required": "{{webUIName}} Yêu cầu Backend", "*Prompt node ID(s) are required for image generation": "*ID nút Prompt là bắt buộc để tạo ảnh", + "1 hour before": "", "1 Source": "", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "", "A discussion channel where access is controlled by groups and permissions": "", "A new version (v{{LATEST_VERSION}}) is now available.": "Một phiên bản mới (v{{LATEST_VERSION}}) đã có sẵn.", @@ -201,6 +206,7 @@ "Ask a question": "Đặt câu hỏi", "Assistant": "Trợ lý", "Async Embedding Processing": "", + "At time of event": "", "Attach File From Knowledge": "", "Attach Files": "", "Attach Knowledge": "", @@ -275,6 +281,7 @@ "Bypass Web Loader": "", "Cache Base Model List": "", "Calendar": "Lịch", + "Calendar deleted": "", "Calendars": "", "Call": "Gọi", "Call feature is not supported when using Web STT engine": "Tính năng gọi điện không được hỗ trợ khi sử dụng công cụ Web STT", @@ -412,6 +419,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "Kết nối với các máy chủ công cụ bên ngoài tương thích OpenAPI của riêng bạn.", "Connected ({{type}})": "", "Connection failed": "Kết nối thất bại", + "Connection lost. Reconnecting...": "", "Connection successful": "Kết nối thành công", "Connection Type": "", "Connections": "Kết nối", @@ -524,6 +532,8 @@ "Delete All Chats": "Xóa mọi cuộc Chat", "Delete all contents inside this folder": "", "Delete automation?": "", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "Xóa chat", "Delete chat?": "Xóa chat?", "Delete Event": "", @@ -885,6 +895,7 @@ "Failed to connect to {{URL}} terminal server": "", "Failed to copy link": "", "Failed to create API Key.": "Lỗi khởi tạo API Key", + "Failed to delete calendar": "", "Failed to delete note": "", "Failed to download image": "", "Failed to extract content from the file: {{error}}": "", @@ -1624,6 +1635,7 @@ "Reasoning Effort": "Nỗ lực Suy luận", "Reasoning Tags": "", "Recently Used": "", + "Reconnected": "", "Record": "", "Record voice": "Ghi âm", "Redirecting you to Open WebUI Community": "Đang chuyển hướng bạn đến Cộng đồng OpenWebUI", @@ -1647,6 +1659,7 @@ "Relevance": "Mức độ liên quan", "Relevance Threshold": "", "Remember Dismissal": "", + "Reminder": "", "Remove": "Xóa", "Remove {{MODELID}} from list.": "", "Remove action": "", @@ -1892,7 +1905,10 @@ "Start a new conversation": "", "Start of the channel": "Đầu kênh", "Start Tag": "", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "", + "Starting now": "", "State": "", "Status": "", "Status cleared successfully": "", @@ -2005,6 +2021,7 @@ "This will delete {{NAME}} and all its contents.": "Hành động này sẽ xóa {{NAME}}tất cả nội dung của nó.", "This will delete all models including custom models": "Hành động này sẽ xóa tất cả các mô hình bao gồm cả các mô hình tùy chỉnh", "This will delete all models including custom models and cannot be undone.": "Hành động này sẽ xóa tất cả các mô hình bao gồm cả các mô hình tùy chỉnh và không thể hoàn tác.", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Hành động này sẽ đặt lại cơ sở kiến thức và đồng bộ hóa tất cả các tệp. Bạn có muốn tiếp tục không?", "Thorough explanation": "Giải thích kỹ lưỡng", "Thought": "", @@ -2091,6 +2108,7 @@ "Unloads {{FROM_NOW}}": "", "Unlock mysteries": "Mở khóa những bí ẩn", "Unpin": "Bỏ ghim", + "Unpin from Sidebar": "", "Unravel secrets": "Làm sáng tỏ những bí mật", "Unshare Chat": "", "Unsupported file type.": "", diff --git a/src/lib/i18n/locales/zh-CN/translation.json b/src/lib/i18n/locales/zh-CN/translation.json index c8e04258c2..ce09ad948c 100644 --- a/src/lib/i18n/locales/zh-CN/translation.json +++ b/src/lib/i18n/locales/zh-CN/translation.json @@ -32,8 +32,13 @@ "{{user}}'s Chats": "{{user}} 的对话记录", "{{webUIName}} Backend Required": "{{webUIName}} 需要后端服务", "*Prompt node ID(s) are required for image generation": "*图片生成需要提示词节点 ID", + "1 hour before": "", "1 Source": "1 个引用来源", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "刚刚", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "成员可加入的协作频道", "A discussion channel where access is controlled by groups and permissions": "由用户组控制的讨论频道", "A new version (v{{LATEST_VERSION}}) is now available.": "新版本(v{{LATEST_VERSION}})现已发布", @@ -201,6 +206,7 @@ "Ask a question": "提问", "Assistant": "助手", "Async Embedding Processing": "异步嵌入处理", + "At time of event": "", "Attach File From Knowledge": "引用知识库中的文件", "Attach Files": "添加文件", "Attach Knowledge": "引用知识库", @@ -275,6 +281,7 @@ "Bypass Web Loader": "绕过网页加载器", "Cache Base Model List": "缓存基础模型列表", "Calendar": "日历", + "Calendar deleted": "", "Calendars": "", "Call": "语音通话", "Call feature is not supported when using Web STT engine": "使用 Web 语音转文字引擎时不支持语音通话功能", @@ -412,6 +419,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "连接到符合 OpenAPI 规范的外部工具服务器", "Connected ({{type}})": "已连接({{type}})", "Connection failed": "连接失败", + "Connection lost. Reconnecting...": "", "Connection successful": "连接成功", "Connection Type": "连接类型", "Connections": "外部连接", @@ -524,6 +532,8 @@ "Delete All Chats": "删除所有对话记录", "Delete all contents inside this folder": "删除此分组内的所有内容", "Delete automation?": "要删除此自动化吗?", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "删除对话记录", "Delete chat?": "要删除此对话记录吗?", "Delete Event": "", @@ -885,6 +895,7 @@ "Failed to connect to {{URL}} terminal server": "无法连接到终端服务器:{{URL}}", "Failed to copy link": "复制链接失败", "Failed to create API Key.": "创建接口密钥失败", + "Failed to delete calendar": "", "Failed to delete note": "删除笔记失败", "Failed to download image": "图片下载失败", "Failed to extract content from the file: {{error}}": "文件内容提取失败:{{error}}", @@ -1624,6 +1635,7 @@ "Reasoning Effort": "推理努力 (Reasoning Effort)", "Reasoning Tags": "推理过程标签", "Recently Used": "最近使用", + "Reconnected": "", "Record": "录制", "Record voice": "录音", "Redirecting you to Open WebUI Community": "正在将您重定向到 Open WebUI 社区", @@ -1647,6 +1659,7 @@ "Relevance": "相关性", "Relevance Threshold": "相关性阈值", "Remember Dismissal": "记住关闭状态", + "Reminder": "", "Remove": "移除", "Remove {{MODELID}} from list.": "从列表中移除 {{MODELID}}", "Remove action": "删除当前操作", @@ -1892,7 +1905,10 @@ "Start a new conversation": "开始新对话", "Start of the channel": "频道起点", "Start Tag": "起始标签", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "正在启动内核...", + "Starting now": "", "State": "状态", "Status": "状态", "Status cleared successfully": "状态已清除", @@ -2005,6 +2021,7 @@ "This will delete {{NAME}} and all its contents.": "这将删除{{NAME}}及其所有内容。", "This will delete all models including custom models": "这将删除所有模型,包括自定义模型", "This will delete all models including custom models and cannot be undone.": "这将删除所有模型,包括自定义模型,且无法撤销。", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "这将重置知识库并同步所有文件。确认继续?", "Thorough explanation": "解释详尽", "Thought": "思考过程", @@ -2091,6 +2108,7 @@ "Unloads {{FROM_NOW}}": "{{FROM_NOW}} 后卸载", "Unlock mysteries": "解码未知", "Unpin": "取消置顶", + "Unpin from Sidebar": "", "Unravel secrets": "冲破奥秘", "Unshare Chat": "取消分享对话", "Unsupported file type.": "不支持的文件类型", diff --git a/src/lib/i18n/locales/zh-TW/translation.json b/src/lib/i18n/locales/zh-TW/translation.json index f98a2bdb76..50d352a96f 100644 --- a/src/lib/i18n/locales/zh-TW/translation.json +++ b/src/lib/i18n/locales/zh-TW/translation.json @@ -32,8 +32,13 @@ "{{user}}'s Chats": "{{user}} 的對話", "{{webUIName}} Backend Required": "需要提供 {{webUIName}} 後端", "*Prompt node ID(s) are required for image generation": "* 產生圖片需要提示詞節點 ID", + "1 hour before": "", "1 Source": "1 個來源", + "10 minutes before": "", + "15 minutes before": "", "1m_time_ago": "剛剛", + "30 minutes before": "", + "5 minutes before": "", "A collaboration channel where people join as members": "成員可加入的協作頻道", "A discussion channel where access is controlled by groups and permissions": "由權限組控制的討論頻道", "A new version (v{{LATEST_VERSION}}) is now available.": "新版本 (v{{LATEST_VERSION}}) 已釋出。", @@ -201,6 +206,7 @@ "Ask a question": "提出問題", "Assistant": "助理", "Async Embedding Processing": "非同步嵌入處理", + "At time of event": "", "Attach File From Knowledge": "從知識庫附加檔案", "Attach Files": "新增檔案", "Attach Knowledge": "附加知識庫", @@ -275,6 +281,7 @@ "Bypass Web Loader": "繞過網頁載入器", "Cache Base Model List": "快取基礎模型清單", "Calendar": "日曆", + "Calendar deleted": "", "Calendars": "", "Call": "通話", "Call feature is not supported when using Web STT engine": "使用網頁語音辨識 (Web STT) 引擎時不支援通話功能", @@ -412,6 +419,7 @@ "Connect to your own OpenAPI compatible external tool servers.": "連線至您自有或其他與 OpenAPI 相容的外部工具伺服器。", "Connected ({{type}})": "已連線({{type}})", "Connection failed": "連線失敗", + "Connection lost. Reconnecting...": "", "Connection successful": "連線成功", "Connection Type": "連線類型", "Connections": "連線", @@ -524,6 +532,8 @@ "Delete All Chats": "刪除所有對話紀錄", "Delete all contents inside this folder": "刪除此資料夾內的所有內容", "Delete automation?": "要刪除此自動化嗎?", + "Delete calendar": "", + "Delete Calendar": "", "Delete Chat": "刪除對話紀錄", "Delete chat?": "刪除對話紀錄?", "Delete Event": "", @@ -885,6 +895,7 @@ "Failed to connect to {{URL}} terminal server": "無法連線至終端伺服器:{{URL}}", "Failed to copy link": "複製連結失敗", "Failed to create API Key.": "建立 API 金鑰失敗。", + "Failed to delete calendar": "", "Failed to delete note": "刪除筆記失敗", "Failed to download image": "圖片下載失敗", "Failed to extract content from the file: {{error}}": "檔案內容擷取失敗:{{error}}", @@ -1624,6 +1635,7 @@ "Reasoning Effort": "推理程度", "Reasoning Tags": "推理標籤", "Recently Used": "最近使用", + "Reconnected": "", "Record": "錄製", "Record voice": "錄音", "Redirecting you to Open WebUI Community": "正在將您重導向至 Open WebUI 社群", @@ -1647,6 +1659,7 @@ "Relevance": "相關性", "Relevance Threshold": "相關性閾值", "Remember Dismissal": "記住關閉狀態", + "Reminder": "", "Remove": "移除", "Remove {{MODELID}} from list.": "從清單中移除 {{MODELID}}", "Remove action": "刪除目前操作", @@ -1892,7 +1905,10 @@ "Start a new conversation": "開始新對話", "Start of the channel": "頻道起點", "Start Tag": "起始標籤", + "Starting in {{count}} minutes_other": "", + "Starting in 1 minute": "", "Starting kernel...": "正在啟動核心…", + "Starting now": "", "State": "狀態", "Status": "狀態", "Status cleared successfully": "狀態已清除", @@ -2005,6 +2021,7 @@ "This will delete {{NAME}} and all its contents.": "這將會刪除 {{NAME}}其所有內容。", "This will delete all models including custom models": "這將刪除所有模型,包括自訂模型", "This will delete all models including custom models and cannot be undone.": "這將刪除所有模型,包括自訂模型,且無法復原。", + "This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "", "This will reset the knowledge base and sync all files. Do you wish to continue?": "這將重設知識庫並同步所有檔案。您確定要繼續嗎?", "Thorough explanation": "詳細解釋", "Thought": "思考過程", @@ -2091,6 +2108,7 @@ "Unloads {{FROM_NOW}}": "於 {{FROM_NOW}} 後解除載入", "Unlock mysteries": "解鎖謎題", "Unpin": "取消釘選", + "Unpin from Sidebar": "", "Unravel secrets": "揭開秘密", "Unshare Chat": "取消分享對話", "Unsupported file type.": "不支援的檔案類型", From 0542df147a90565cfec224af46e589200ddd9553 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 15:52:33 +0900 Subject: [PATCH 327/334] refac --- src/lib/components/chat/ChatControls.svelte | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/lib/components/chat/ChatControls.svelte b/src/lib/components/chat/ChatControls.svelte index 3cbcc7ed87..8ac5f06617 100644 --- a/src/lib/components/chat/ChatControls.svelte +++ b/src/lib/components/chat/ChatControls.svelte @@ -72,7 +72,10 @@ $: showControlsTab = $user?.role === 'admin' || ($user?.permissions?.chat?.controls ?? true); $: showFilesTab = - !!$selectedTerminalId || + ($selectedTerminalId && + (($terminalServers ?? []).some((t) => t.id && t.id === $selectedTerminalId) || + $user?.role === 'admin' || + ($user?.permissions?.features?.direct_tool_servers ?? true))) || (codeInterpreterEnabled && $config?.code?.interpreter_engine !== 'jupyter'); $: showOverviewTab = hasMessages; @@ -96,13 +99,22 @@ } // Auto-open Files tab when a terminal is selected (suppress panel open when full-screen) - $: if ($selectedTerminalId) { + $: if ($selectedTerminalId && showFilesTab) { activeTab = 'files'; if (largeScreen) { showControls.set(true); } } + // Clear selected direct terminal if user lost permission + $: if ( + $selectedTerminalId && + !($terminalServers ?? []).some((t) => t.id && t.id === $selectedTerminalId) && + !($user?.role === 'admin' || ($user?.permissions?.features?.direct_tool_servers ?? true)) + ) { + selectedTerminalId.set(null); + } + // Attach a terminal file to the chat input const handleTerminalAttach = async (blob: Blob, name: string, contentType: string) => { const tempItemId = uuidv4(); From 65f55847a144a1c61775c0097116932f716ee7fa Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 16:04:48 +0900 Subject: [PATCH 328/334] refac --- backend/open_webui/retrieval/utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index b9bfcc12c8..fb5a46c2b0 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -172,6 +172,11 @@ def _is_text_content_type(content_type: str) -> bool: def get_content_from_url(request, url: str) -> str: + from open_webui.retrieval.web.utils import validate_url + + # Validate URL before making any request (blocks private IPs, non-HTTP, filter list) + validate_url(url) + # Streamed GET to check Content-Type without downloading the body. try: response = requests.get(url, stream=True, timeout=30) From 116eb7fc5501e43d217489776d11792e4d2fe2ef Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 16:05:26 +0900 Subject: [PATCH 329/334] refac --- backend/open_webui/utils/oauth.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 9a35e30c3f..47302e7535 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -1922,10 +1922,10 @@ class OAuthManager: users_to_logout.append(user) if not users_to_logout and sid: - log.info(f'Back-channel logout: no user found by sub, sid-based lookup not yet supported (sid={sid})') + log.debug(f'Back-channel logout: no user found by sub, sid-based lookup not yet supported (sid={sid})') if not users_to_logout: - log.info(f'Back-channel logout: no matching user for provider={matched_provider}, sub={sub}, sid={sid}') + log.debug(f'Back-channel logout: no matching user for provider={matched_provider}, sub={sub}, sid={sid}') return JSONResponse(status_code=200, content={}) # 9. Revoke tokens and delete sessions From 085d3cb1c9e5046e0fa165e153bf821f53b795b9 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 16:16:48 +0900 Subject: [PATCH 330/334] refac --- src/lib/utils/index.ts | 61 +++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index dc23620a12..38e7636e25 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -677,7 +677,9 @@ export const calculateSHA256 = async (file) => { export const getImportOrigin = (_chats) => { // Check what external service chat imports are from - if ('mapping' in _chats[0]) { + // ChatGPT exports may include folder/project metadata entries without 'mapping', + // so we check if ANY item has a 'mapping' key instead of only the first one. + if (_chats.some((chat) => 'mapping' in chat)) { return 'openai'; } return 'webui'; @@ -706,6 +708,21 @@ export const getUserPosition = async (raw = false) => { } }; +const extractOpenAIMessageContent = (message): string => { + // Extract text content from a ChatGPT message, handling various content formats + // (string parts, object parts like DALL-E images, text field fallback) + try { + const parts = message?.['content']?.['parts']; + if (Array.isArray(parts)) { + const textParts = parts.filter((p) => typeof p === 'string'); + if (textParts.length > 0) return textParts.join('\n'); + } + return message?.['content']?.['text'] || ''; + } catch { + return ''; + } +}; + const convertOpenAIMessages = (convo) => { // Parse OpenAI chat messages and create chat dictionary for creating new chats const mapping = convo['mapping']; @@ -726,15 +743,18 @@ const convertOpenAIMessages = (convo) => { // Skip chat messages with no content continue; } else { + const role = message['message']?.['author']?.['role']; + // Skip system and tool messages — they don't map to user/assistant + if (role === 'system' || role === 'tool') { + continue; + } + const new_chat = { id: message_id, parentId: lastId, childrenIds: message['children'] || [], - role: message['message']?.['author']?.['role'] !== 'user' ? 'assistant' : 'user', - content: - message['message']?.['content']?.['parts']?.[0] || - message['message']?.['content']?.['text'] || - '', + role: role !== 'user' ? 'assistant' : 'user', + content: extractOpenAIMessageContent(message['message']), model: 'gpt-3.5-turbo', done: true, context: null @@ -747,6 +767,12 @@ const convertOpenAIMessages = (convo) => { } } + // Fix up the last message's childrenIds to be empty (it's the leaf node in our + // linear chain regardless of what the original tree structure had) + if (messages.length > 0) { + messages[messages.length - 1].childrenIds = []; + } + const history: Record = {}; messages.forEach((obj) => (history[obj.id] = obj)); @@ -773,18 +799,6 @@ const validateChat = (chat) => { return false; } - // Last message's children should be an empty array - const lastMessage = messages[messages.length - 1]; - if (lastMessage.childrenIds.length !== 0) { - return false; - } - - // First message's parent should be null - const firstMessage = messages[0]; - if (firstMessage.parentId !== null) { - return false; - } - // Every message's content should be a string for (const message of messages) { if (typeof message.content !== 'string') { @@ -799,7 +813,15 @@ export const convertOpenAIChats = (_chats) => { // Create a list of dictionaries with each conversation from import const chats = []; let failed = 0; + let skipped = 0; for (const convo of _chats) { + // Skip folder/project metadata entries that lack a 'mapping' key + if (!('mapping' in convo)) { + skipped++; + console.log('Skipping non-conversation entry (folder/project):', convo['title'] ?? convo['id']); + continue; + } + const chat = convertOpenAIMessages(convo); if (validateChat(chat)) { @@ -815,6 +837,9 @@ export const convertOpenAIChats = (_chats) => { } } console.log(failed, 'Conversations could not be imported'); + if (skipped > 0) { + console.log(skipped, 'Non-conversation entries (folders/projects) were skipped'); + } return chats; }; From 3b821e1f3a54d56fcde9ee88ca977c8a0ff3caea Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 16:32:17 +0900 Subject: [PATCH 331/334] refac --- src/lib/utils/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 38e7636e25..1820e70481 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -818,7 +818,10 @@ export const convertOpenAIChats = (_chats) => { // Skip folder/project metadata entries that lack a 'mapping' key if (!('mapping' in convo)) { skipped++; - console.log('Skipping non-conversation entry (folder/project):', convo['title'] ?? convo['id']); + console.log( + 'Skipping non-conversation entry (folder/project):', + convo['title'] ?? convo['id'] + ); continue; } From 493f238431e5b06e488cb5f9607bab7465301300 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 21 Apr 2026 16:46:02 +0900 Subject: [PATCH 332/334] refac --- CHANGELOG.md | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d5f34d74f..4e0b35b16b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,26 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- 🖥️ **Native desktop app availability.** Open WebUI is now available as a cross-platform desktop app with local model support, multi-server switching, and offline-ready usage after first launch. [#8262](https://github.com/open-webui/open-webui/issues/8262), [Desktop](https://github.com/open-webui/desktop) -- 🤖 **Scheduled chat automations.** Users can now create, schedule, run, and manage recurring automations from both the dedicated automations page and built-in chat tools, with execution logs, direct run controls, and permission-aware access control for user and group policies. [#23303](https://github.com/open-webui/open-webui/pull/23303), [Commit](https://github.com/open-webui/open-webui/commit/5a2ff8b2e5b6f55a20f7ed491f818490eb535ea7), [Commit](https://github.com/open-webui/open-webui/commit/d30a0531d4add045c21a2368d6321a9b1906865f), [Commit](https://github.com/open-webui/open-webui/commit/bae5ff938ac88a3a647cc31ca8db1101015ae18b), [Commit](https://github.com/open-webui/open-webui/commit/588b81eedaacbfd7394b707ae1600d9fb729b809..674695918e5e3e1811314ce2a082c5bbb42d76b2) +- 🖥️ **Official Open WebUI Desktop App.** Open WebUI is now available as a native desktop app for Mac, Windows, and Linux. No Docker, no terminal, no setup. Runs Open WebUI locally without any server setup, or connects to your existing remote Open WebUI instances. Switch between multiple servers instantly from the sidebar. Comes with a system-wide floating chat bar (Shift+Cmd+I on macOS, Shift+Ctrl+I on Windows/Linux), system-wide push-to-talk, offline support after first launch, automatic updates, and zero telemetry. [#8262](https://github.com/open-webui/open-webui/issues/8262), [Desktop](https://github.com/open-webui/desktop) +- 🤖 **Scheduled chat automations.** You can now schedule the AI to run tasks automatically on a recurring basis: daily digests, periodic reports, anything you'd otherwise need to remember to ask for. Create and manage automations from the Automations page or directly in chat, with full run history and manual trigger controls. [#23303](https://github.com/open-webui/open-webui/pull/23303), [Commit](https://github.com/open-webui/open-webui/commit/5a2ff8b2e5b6f55a20f7ed491f818490eb535ea7), [Commit](https://github.com/open-webui/open-webui/commit/d30a0531d4add045c21a2368d6321a9b1906865f), [Commit](https://github.com/open-webui/open-webui/commit/bae5ff938ac88a3a647cc31ca8db1101015ae18b), [Commit](https://github.com/open-webui/open-webui/commit/588b81eedaacbfd7394b707ae1600d9fb729b809..674695918e5e3e1811314ce2a082c5bbb42d76b2) - 🧰 **Automation tools in chat.** Built-in chat tools can now create, update, list, pause, and delete scheduled automations directly in conversation when automation access is enabled. [Commit](https://github.com/open-webui/open-webui/commit/588b81eedaacbfd7394b707ae1600d9fb729b809..674695918e5e3e1811314ce2a082c5bbb42d76b2) -- 🤖 **Automation model selection reliability.** Automations created from chat now consistently use the calling model, avoiding mismatches when tool calls run under different model contexts. [Commit](https://github.com/open-webui/open-webui/commit/e709d6812f7fba246c4b7907f9fa41f751717566), [Commit](https://github.com/open-webui/open-webui/commit/398718d5059ce2a5614e9e124f20ef48b843ce42), [#23812](https://github.com/open-webui/open-webui/pull/23812) - ⏱️ **Automation scheduling limits.** Administrators can now set "AUTOMATION_MAX_COUNT" and "AUTOMATION_MIN_INTERVAL" to limit how many automations each non-admin user can create and prevent overly frequent schedules that could overload the system. [Commit](https://github.com/open-webui/open-webui/commit/406251c2f358ffabce4d631c98c6f2c879feae5c) -- 🧭 **Global automations toggle.** Administrators can now disable automations system-wide with the "ENABLE_AUTOMATIONS" setting, which hides automation pages and tools and pauses background automation processing until it is re-enabled. [Commit](https://github.com/open-webui/open-webui/commit/42694c7c0cc8ba586c1dd364ecfaa0b4080b6cad) - 📋 **Task management tool.** AI models can now create, update, and track tasks within a chat conversation, breaking down complex requests into manageable steps with real-time status updates. [Commit](https://github.com/open-webui/open-webui/commit/bcb71bb5206ac01d97a39fde8ecf0e0541dde636) -- 🗓️ **Calendar workspace and event management.** Users can now manage personal and shared calendars from a dedicated Calendar page, create and edit events (including recurring events), and view scheduled automations directly alongside calendar activity. [#23880](https://github.com/open-webui/open-webui/pull/23880) -- 🔐 **Calendar permission controls.** Administrators can now control calendar access through feature permissions, so calendar pages, APIs, and built-in calendar tools are available only to users and groups with calendar access enabled. [Commit](https://github.com/open-webui/open-webui/commit/5afc258c5b13f456be528420513ade546c5e86f9), [Commit](https://github.com/open-webui/open-webui/commit/37eba1c5a66b3145c122a6b40e5c29707526d121) -- 🗑️ **Calendar deletion controls.** Calendar sidebar entries now include a delete action with confirmation, allowing users to remove custom calendars directly from the Calendar page. [Commit](https://github.com/open-webui/open-webui/commit/51627555bf356c8ec663f4d2f43f2f013eadbce4) +- 🗓️ **Calendar workspace and event management.** Open WebUI now has a full Calendar workspace. Create and manage events, set up recurring schedules, get reminders via in-app toasts or browser notifications, and see your scheduled automations alongside your calendar. [#23880](https://github.com/open-webui/open-webui/pull/23880) - 🔔 **Calendar reminders and alerts.** Calendar events now support reminder options from no alert up to one hour before start time, with upcoming alerts delivered through in-app toasts, browser notifications, and optional webhooks while avoiding duplicate sends. [Commit](https://github.com/open-webui/open-webui/commit/e5b5a174265d6710e986f6534ee7e3b2923233be) - ⚙️ **Scheduler reminder configuration.** Administrators can now configure calendar reminder processing with "SCHEDULER_POLL_INTERVAL" and "CALENDAR_ALERT_LOOKAHEAD_MINUTES", while existing "AUTOMATION_POLL_INTERVAL" setups continue to work as a legacy fallback. [Commit](https://github.com/open-webui/open-webui/commit/e5b5a174265d6710e986f6534ee7e3b2923233be) -- 🗓️ **Unified calendar header controls.** The Calendar page now uses a single top navigation bar for date navigation, view selection, and quick event creation, with improved mobile behavior and label truncation for tighter screens. [Commit](https://github.com/open-webui/open-webui/commit/4e31fa4427037c0ffd4ad704308203639bf05df8), [Commit](https://github.com/open-webui/open-webui/commit/3e3f138d9323987a41b1e3c17721a0047cf8e40f) -- 🧰 **Dedicated task checklist tools.** Built-in task tracking exposes separate tools for creating task lists and updating individual task statuses, giving multi-step chats clearer progress control. [Commit](https://github.com/open-webui/open-webui/commit/a35926261646f8897ba71da1572ed5dff802e3be) - ☁️ **Azure responses support.** Azure OpenAI connections now support the newer "/openai/v1" format, enabling chat, responses, and proxy calls to work correctly with that endpoint style. [#23484](https://github.com/open-webui/open-webui/pull/23484) - 🤖 **Ollama responses support.** The Ollama proxy now supports the Responses API, letting clients use "/v1/responses" directly with Ollama-hosted models through Open WebUI. [#23483](https://github.com/open-webui/open-webui/pull/23483) - 🧩 **Responses tool output rendering.** Built-in tool outputs in Responses API flows now render more consistently so downstream chat output is easier to interpret. [Commit](https://github.com/open-webui/open-webui/commit/e695d854f2d11fada84d5fbec8d3edea4e468e19), [#23482](https://github.com/open-webui/open-webui/pull/23482) - 🔎 **Responses citation visibility.** Responses API flows now emit citation sources more consistently, making linked references easier to preserve and display in chat output. [Commit](https://github.com/open-webui/open-webui/commit/e695d854f2d11fada84d5fbec8d3edea4e468e19), [#23774](https://github.com/open-webui/open-webui/issues/23774) - 📎 **Attach previously uploaded files.** The chat input menu now includes a Files tab for browsing and attaching previously uploaded files, eliminating the need to re-upload files you have already shared. [Commit](https://github.com/open-webui/open-webui/commit/edb8971c7dbd974322c3207c4655ff66479c3ee2) -- 🖥️ **Terminal session tracking.** Open Terminal now tracks the current working directory per chat session, so relative paths and navigation work correctly across multiple interactions. [Commit](https://github.com/open-webui/open-webui/commit/a06685a47b89fb19dd6124fbe391ff78b54f451d), [Commit](https://github.com/open-webui/open-webui/commit/6512e085c4e56897dd49e56aff5d616820a962f3) - 🧷 **Default model terminal selection.** Workspace model editors can now preselect an Open Terminal connection, so new chats automatically start with the model’s configured terminal ready to use. [Commit](https://github.com/open-webui/open-webui/commit/47d413ce7b2a006a8126f4a9055b13e5fcb33a1d), [#23605](https://github.com/open-webui/open-webui/issues/23605) - 🎙️ **Mistral TTS support.** Mistral can now be used as a text-to-speech provider, with admin settings for the API key, base URL, voices, and model selection. [Commit](https://github.com/open-webui/open-webui/commit/4cee67e2be0c80a0b501073ea49a80d13efd1c41) - 🎧 **STT preprocessing bypass option.** Administrators can now enable "AUDIO_STT_SKIP_PREPROCESSING" to send audio files directly to the speech-to-text backend, reducing memory and CPU consumption during large uploads for better transcription performance and stability on constrained deployments. [#23661](https://github.com/open-webui/open-webui/pull/23661) @@ -38,7 +31,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 📌 **Recently used emojis.** The emoji picker now shows your most recently used emojis at the top, making it faster to find emojis you use often. [Commit](https://github.com/open-webui/open-webui/commit/64da99a32218171d41b3af5acc14783de8dbdf49) - 👆 **Swipe to reply on mobile.** Swiping right on a message now triggers a reply, making it easier to respond on touch devices with a natural gesture. [Commit](https://github.com/open-webui/open-webui/commit/012ce95f27d57bea8911bd63bfb923443c5797ae) - 📱 **Screen-awake voice recording.** Voice recording now keeps the screen awake during active dictation and safely re-acquires wake lock after visibility changes, helping prevent long transcriptions from being cut off on mobile devices. [#23145](https://github.com/open-webui/open-webui/issues/23145) -- ✨ **Improved task list visibility.** The task list automatically hides once all tasks are complete and generation is finished, keeping the chat interface cleaner. [Commit](https://github.com/open-webui/open-webui/commit/0ad397c0482004173d4a8bf4722100acc43db454), [Commit](https://github.com/open-webui/open-webui/commit/4b35d70078a2d7a322566699a43594b3c10b2dda) - 🔔 **Unread chat indicators.** Sidebar chats now show unread status and are marked as read when opened, making it easier to spot conversations with new activity. [Commit](https://github.com/open-webui/open-webui/commit/0638b9f56ce1ba8a496d0e84da2e7fa178b01a3f) - 🔌 **WebSocket reconnect status feedback.** Open WebUI now warns when the real-time connection drops and confirms when it reconnects, while avoiding a reconnect message on the initial page load. [Commit](https://github.com/open-webui/open-webui/commit/1824e69a70e756cfcf543a9fbe4b0780d9b57292) - 📍 **Pinned notes in sidebar.** Notes can now be pinned to the sidebar for quick access, and you can also create a new note directly from the pinned notes section. [Commit](https://github.com/open-webui/open-webui/commit/ecd74f220c7dd671d5705189a3f4493a3868c8bf), [Commit](https://github.com/open-webui/open-webui/commit/f1be85d997439b49fc143d2bcd2dc710f44446c8) @@ -48,11 +40,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 🎨 **Theme updates.** Other windows can now update the app theme directly, keeping the interface in sync when theme changes are triggered externally. [Commit](https://github.com/open-webui/open-webui/commit/9f1b279e88bd22dfff4d2531209536dea6a2f65e) - 🚀 **Async performance and responsiveness improvements.** The core backend database and request paths now run asynchronously across the application, massively improving responsiveness and performance under concurrent load and reducing request blocking during heavy activity. [Commit](https://github.com/open-webui/open-webui/commit/27169124f220e5cea21c88601c731c3749496ab0), [Commit](https://github.com/open-webui/open-webui/commit/8936721414a17832852a90f3ee592af5a8b7232d) - ⚡ **Drawer performance and memory optimization.** Drawer interactions now stay smoother over long sessions by removing stale keyboard listeners on teardown, which reduces memory growth and avoids accumulated event handling overhead. [#23724](https://github.com/open-webui/open-webui/pull/23724#issuecomment-4245840810) -- 🚀 **Chat history memory culling.** Long conversations now stay much more responsive by rendering a smaller message window and unloading off-screen messages with spacer-based virtualization, significantly reducing memory pressure and UI freezing on heavy chats and mobile devices. [#23067](https://github.com/open-webui/open-webui/issues/23067), [Commit](https://github.com/open-webui/open-webui/commit/026903399be73ac4b6c226647110e5662d043a50), [Commit](https://github.com/open-webui/open-webui/commit/9dccd29c94875e6f0ac373c5802cb183296e47ff) +- 🚀 **Chat history memory culling.** Long conversations now stay responsive no matter how many messages they contain. Off-screen messages are unloaded automatically and reloaded as you scroll, keeping memory usage low and the UI smooth on both desktop and mobile. [#23067](https://github.com/open-webui/open-webui/issues/23067), [Commit](https://github.com/open-webui/open-webui/commit/026903399be73ac4b6c226647110e5662d043a50), [Commit](https://github.com/open-webui/open-webui/commit/9dccd29c94875e6f0ac373c5802cb183296e47ff) - 🧵 **Async file and knowledge processing performance.** File processing, knowledge reindexing, and channel message helper paths now consistently await async operations, preventing skipped processing steps and improving reliability and performance of indexing and tool responses. [Commit](https://github.com/open-webui/open-webui/commit/de27a121511a31606f250ba4033490797216a0eb) - 🚀 **Persistent chat payload efficiency.** Persisted chats now use server-side history loading instead of repeatedly resending full message payloads, improving multimodal performance and reducing stale-history overwrite risk across devices. [#19064](https://github.com/open-webui/open-webui/issues/19064), [Commit](https://github.com/open-webui/open-webui/commit/18fe17127a7175579506e7456d3e5aba201371e6), [Commit](https://github.com/open-webui/open-webui/commit/cf4218e688def6f11d195aeda6665ae5b5376b67) - 🧵 **Non-blocking file storage operations.** Uploading, reading, transcribing, and deleting files now offloads storage I/O to background threads, keeping the application responsive during file-heavy workflows. [Commit](https://github.com/open-webui/open-webui/commit/4866bec0f238198a721c952fe18dd04ba643be33) -- 🏃 **Faster automation list loading.** The automations page now loads more smoothly by batching latest-run lookups and avoiding duplicate initial fetches. [Commit](https://github.com/open-webui/open-webui/commit/09f6d7ba57d2aaad83ad0d29d005feb7157776a1) - 🏎️ **Streaming response performance.** Streaming responses now process each output line in a single step instead of two separate yields, reducing async overhead and improving responsiveness during long-running generations. [#23266](https://github.com/open-webui/open-webui/pull/23266) - 🔎 **Faster mention parsing.** Chat text with HTML-like content, file paths, or tool output now parses mentions more efficiently, which helps keep typing and rendering responsive in messages that contain many '<' characters. [#23551](https://github.com/open-webui/open-webui/pull/23551) - 🧪 **Code block rendering performance.** Code blocks now reuse a shared HTML unescape helper, reducing extra browser work when displaying encoded output in chat. [#23553](https://github.com/open-webui/open-webui/pull/23553) @@ -99,6 +90,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 🧩 **Richer Anthropic tool results.** Anthropic-compatible tool calls now preserve more tool result content types, including images and structured search or document outputs, so models can use fuller tool context instead of receiving only plain text fragments. [#23188](https://github.com/open-webui/open-webui/issues/23188), [Commit](https://github.com/open-webui/open-webui/commit/40f5b3d135190dc9a2d8e94dbb1b2cbcbd829132) - 🖼️ **ComfyUI request reliability.** ComfyUI image generation and editing now use shared async connections with consistent SSL handling, making image uploads and workflow runs more reliable under concurrent load. [Commit](https://github.com/open-webui/open-webui/commit/5944eda0ff25a284f7157252683bccede741cbe7) - 🎛️ **Reranking batch size control.** Administrators can now set "RAG_RERANKING_BATCH_SIZE" in Documents settings to control reranking workload size, helping balance retrieval speed and resource usage for their deployment. [Commit](https://github.com/open-webui/open-webui/commit/4d2f18981051205016bd24d39521e25a33581225) +- 🔗 **Shared chat access controls.** You can now control who has access to a shared chat by granting access to specific users or groups, instead of sharing with anyone who has the link. - 🔄 **General improvements.** Various improvements were implemented across the application to enhance performance, stability, and security. - 🌐 **Translation updates.** Translations for Irish, Catalan, German, Simplified Chinese, Hindi, and Portuguese (Brazil) were enhanced and expanded. @@ -111,8 +103,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 🗣️ **Pipeline error detail visibility.** Pipeline inlet and outlet failures now preserve and surface provider error details more reliably in chat error messages, making troubleshooting failed requests much clearer. [Commit](https://github.com/open-webui/open-webui/commit/d5e69f182cd7a6371ab25248f6432b277f83ef23) - 📨 **Shared chat event routing.** Message update and send events now target the chat owner’s event channel, so shared chats receive the correct real-time updates instead of routing events to the acting user. [Commit](https://github.com/open-webui/open-webui/commit/47329b5032ba29716a7e7e973b07c6d9894968e0) - 🔐 **Consistent outbound SSL handling.** External requests for tools, functions, terminals, webhooks, retrieval loaders, audio provider discovery, and OpenAI-compatible embedding calls now consistently apply the configured SSL client setting, improving reliability for deployments that require custom certificate or verification behavior. [Commit](https://github.com/open-webui/open-webui/commit/fd25152076ea7c310e42c9bacc5cd2b544eeae48), [Commit](https://github.com/open-webui/open-webui/commit/56c5bc1d3487020ab886d3332aacc1644c1d6123) -- 🧭 **Scheduled Tasks calendar reliability.** Scheduled Tasks is now handled as a virtual automation calendar that appears only when automation access is available, and calendar selection now filters by stable ID instead of name so event forms behave consistently. [Commit](https://github.com/open-webui/open-webui/commit/1d501cfa3f96b3a9a5f4f7ce996947671fd09f29), [Commit](https://github.com/open-webui/open-webui/commit/24dd5b461eb44d306c823389e0f664c45db042e8) -- 🛡️ **Protected calendar deletion rules.** System and default calendars can no longer be deleted, preventing accidental removal of built-in calendar functionality. [Commit](https://github.com/open-webui/open-webui/commit/51627555bf356c8ec663f4d2f43f2f013eadbce4) - 🖼️ **Image SSL setting support.** Image generation now respects the configured SSL session setting, preventing avoidable connection failures in strict certificate environments. [Commit](https://github.com/open-webui/open-webui/commit/128cf41fcedf2638fc8a6acd850d8b0409be1c4e), [#23777](https://github.com/open-webui/open-webui/issues/23777) - 🗂️ **Folder ownership assignment hardening.** Folder create and update inputs now reject unexpected extra fields, preventing clients from overriding protected values like ownership through mass-assignment payloads. [#23648](https://github.com/open-webui/open-webui/pull/23648) - 🔐 **Knowledge file deletion ownership checks.** Collaborators with knowledge base write access can no longer permanently delete files they do not own, preventing unintended file removal across other linked chats and knowledge bases. [Commit](https://github.com/open-webui/open-webui/commit/914ccf07ef158afe5588b97ed42778c93c439938), [#23636](https://github.com/open-webui/open-webui/pull/23636#issuecomment-4232439454) From 9f61a6f13c5c7668aed894ef12b2711352e1e9c3 Mon Sep 17 00:00:00 2001 From: Tim Baek Date: Tue, 21 Apr 2026 19:37:22 +0900 Subject: [PATCH 333/334] fix --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 27e6faeddf..b6d07a61f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,8 @@ dependencies = [ "python-mimeparse==2.0.0", "sqlalchemy==2.0.48", + "aiosqlite==0.21.0", + "asyncpg==0.30.0", "alembic==1.18.4", "peewee==3.19.0", "peewee-migrate==1.14.3", From f162d4de9077824d613425552f127cf4eb4a38b5 Mon Sep 17 00:00:00 2001 From: Tim Baek Date: Tue, 21 Apr 2026 19:39:44 +0900 Subject: [PATCH 334/334] doc --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e0b35b16b..8049dcca1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ 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.9.1] - 2026-04-21 + +### Fixed + +- 🐛 **Missing `aiosqlite` dependency.** Fixed a startup crash (`ModuleNotFoundError: No module named 'aiosqlite'`) when installing Open WebUI via `pip` or `uv` by adding the missing `aiosqlite` package to `pyproject.toml`. The dependency was listed in `requirements.txt` but not in the published package metadata, so it was not installed automatically. [#23916](https://github.com/open-webui/open-webui/issues/23916) +- 🐛 **Missing `asyncpg` dependency.** Added the missing `asyncpg` package to `pyproject.toml` to prevent the same startup crash for PostgreSQL users. Like `aiosqlite`, it was present in `requirements.txt` but absent from the published package dependencies. + ## [0.9.0] - 2026-04-20 ### Added diff --git a/package-lock.json b/package-lock.json index 8efa79c1b4..e3175ba8a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "open-webui", - "version": "0.9.0", + "version": "0.9.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "open-webui", - "version": "0.9.0", + "version": "0.9.1", "dependencies": { "@azure/msal-browser": "^4.5.0", "@codemirror/lang-javascript": "^6.2.2", diff --git a/package.json b/package.json index bc1a1c5da3..ab246848c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-webui", - "version": "0.9.0", + "version": "0.9.1", "private": true, "scripts": { "dev": "npm run pyodide:fetch && vite dev --host",