Skip to content

Commit eec72c1

Browse files
Merge branch 'open-webui:dev' into dev
2 parents ac48805 + 120409e commit eec72c1

10 files changed

Lines changed: 66 additions & 23 deletions

File tree

backend/open_webui/utils/chat_variables.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def _safe_field(key: str, definition: dict[str, Any]) -> dict[str, Any]:
112112
'type',
113113
}
114114
field = {'key': key}
115-
for field_key in allowed_keys:
115+
for field_key in sorted(allowed_keys):
116116
if field_key in definition:
117117
field[field_key] = definition[field_key]
118118

backend/open_webui/utils/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@ def get_action_priority(action_id):
374374
for filter_id in set(model.pop('filter_ids', [])) | global_filter_ids
375375
if filter_id in enabled_filter_ids
376376
]
377+
# Set order varies per process, and an unstable order defeats the RedisDict content signature.
378+
filter_ids.sort()
377379

378380
model['actions'] = []
379381
for action_id in action_ids:

src/lib/components/automations/AutomationEditor.svelte

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@
9595
};
9696
9797
const formatSchedule = (rrule: string): string => {
98-
if (rrule.includes('COUNT=1')) {
98+
if (/COUNT=1(?!\d)/.test(rrule)) {
9999
const match = rrule.match(/DTSTART:(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})/);
100100
if (match) {
101101
const d = new Date(`${match[1]}-${match[2]}-${match[3]}T${match[4]}:${match[5]}`);
@@ -109,6 +109,9 @@
109109
110110
const parts: Record<string, string> = {};
111111
rrule
112+
.split(/\s+/)
113+
.filter((line) => !line.toUpperCase().startsWith('DTSTART'))
114+
.join('')
112115
.replace('RRULE:', '')
113116
.split(';')
114117
.forEach((part) => {

src/lib/components/automations/ScheduleDropdown.svelte

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@
106106
107107
export const parseRrule = (s: string) => {
108108
// Detect ONCE (COUNT=1 with DTSTART)
109-
if (s.includes('COUNT=1')) {
109+
if (/COUNT=1(?!\d)/.test(s)) {
110110
frequency = 'ONCE';
111111
const match = s.match(/DTSTART:(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})/);
112112
if (match) {
@@ -116,7 +116,10 @@
116116
return;
117117
}
118118
const parts: Record<string, string> = {};
119-
s.replace('RRULE:', '')
119+
s.split(/\s+/)
120+
.filter((line) => !line.toUpperCase().startsWith('DTSTART'))
121+
.join('')
122+
.replace('RRULE:', '')
120123
.split(';')
121124
.forEach((p) => {
122125
const [k, v] = p.split('=');

src/lib/components/chat/FileNav.svelte

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,6 @@
889889
if (!result) throw new Error('Preview failed');
890890
const arrayBuffer = await result.blob.arrayBuffer();
891891
fileDocxData = arrayBuffer;
892-
toast.info($i18n.t('Preview may differ from download.'));
893892
}
894893
} else if (ext === 'xlsx') {
895894
const result = await downloadFileBlob(
@@ -934,7 +933,6 @@
934933
const fallback = await pptxToImages(arrayBuffer);
935934
fileOfficeSlides = fallback.images;
936935
currentSlide = 0;
937-
toast.info($i18n.t('Preview may differ from download.'));
938936
}
939937
}
940938
} catch (e) {

src/lib/components/chat/Messages/TerminalOutputFile.svelte

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@
168168
const result = await blobForPreview();
169169
if (!result) throw new Error(t('Preview failed'));
170170
fileDocxData = await result.blob.arrayBuffer();
171-
toast.info(t('Preview may differ from download.'));
172171
}
173172
} else if (ext === 'xlsx' || ext === 'xls') {
174173
const result = await blobForPreview();
@@ -196,7 +195,6 @@
196195
const { pptxToImages } = await import('$lib/utils/pptxToHtml');
197196
const resultImages = await pptxToImages(arrayBuffer);
198197
fileOfficeSlides = resultImages.images;
199-
toast.info(t('Preview may differ from download.'));
200198
}
201199
}
202200
} else if (terminal) {

src/lib/components/common/PDFViewer.svelte

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,10 @@
386386
void selectPage(selectedPage + (e.key === 'ArrowUp' || e.key === 'ArrowLeft' ? -1 : 1));
387387
};
388388
389+
const focusViewer = () => {
390+
if (!outerContainer?.contains(document.activeElement)) outerContainer?.focus();
391+
};
392+
389393
const loadPdf = async () => {
390394
if (!url && !data) return;
391395
@@ -462,8 +466,6 @@
462466
});
463467
</script>
464468

465-
<svelte:window on:keydown={handleKeyDown} />
466-
467469
<div class="relative {className}">
468470
{#if loading}
469471
<div class="absolute inset-0 flex items-center justify-center">
@@ -480,8 +482,13 @@
480482
? 'overflow-hidden h-full flex items-center justify-center overscroll-contain'
481483
: 'overflow-y-auto h-full'}
482484
bind:this={outerContainer}
485+
role="application"
486+
aria-label={`${itemLabel} viewer`}
487+
tabindex="0"
483488
on:scroll={handleScroll}
484489
on:wheel|nonpassive={handleWheel}
490+
on:pointerdown={focusViewer}
491+
on:keydown={handleKeyDown}
485492
>
486493
<div bind:this={sceneElement} class={singlePage ? '' : 'w-full'}></div>
487494
</div>

src/lib/components/common/PptxPreview.svelte

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@
121121
}
122122
};
123123
124+
const focusPreview = () => {
125+
if (!rootEl?.contains(document.activeElement)) rootEl?.focus();
126+
};
127+
124128
const zoomIn = () => {
125129
if (!pzInstance || !stageEl) return;
126130
pzInstance.zoomTo(stageEl.clientWidth / 2, stageEl.clientHeight / 2, 1.25);
@@ -227,10 +231,13 @@
227231
});
228232
</script>
229233

230-
<svelte:window on:keydown={handleKeyDown} />
231-
232234
<div
233235
bind:this={rootEl}
236+
role="application"
237+
aria-label={`${itemLabel} preview`}
238+
tabindex="0"
239+
on:keydown={handleKeyDown}
240+
on:pointerdown={focusPreview}
234241
class="relative grid {hideThumbs
235242
? 'grid-cols-[minmax(0,1fr)]'
236243
: 'grid-cols-[144px_minmax(0,1fr)]'} min-h-0 bg-transparent text-gray-900 dark:text-gray-100 {className}"

src/lib/components/layout/Sidebar.svelte

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -600,36 +600,57 @@
600600
const MAX_WIDTH = 480;
601601
602602
let isResizing = false;
603+
let activePointerId: number | null = null;
604+
let activeResizer: HTMLElement | null = null;
603605
604606
let startWidth = 0;
605607
let startClientX = 0;
606608
607-
const resizeStartHandler = (e: MouseEvent) => {
609+
const resizeStartHandler = (e: PointerEvent) => {
608610
if ($mobile) return;
611+
612+
e.preventDefault();
609613
isResizing = true;
614+
activePointerId = e.pointerId;
615+
activeResizer = e.currentTarget as HTMLElement;
616+
activeResizer.setPointerCapture?.(e.pointerId);
610617
611618
startClientX = e.clientX;
612619
startWidth = $sidebarWidth ?? 245;
613620
614621
document.body.style.userSelect = 'none';
615622
};
616623
617-
const resizeEndHandler = () => {
624+
const resizeEndHandler = (e?: PointerEvent) => {
618625
if (!isResizing) return;
626+
if (e && activePointerId !== null && e.pointerId !== activePointerId) return;
627+
619628
isResizing = false;
620629
630+
if (activePointerId !== null && activeResizer?.hasPointerCapture?.(activePointerId)) {
631+
activeResizer.releasePointerCapture(activePointerId);
632+
}
633+
activePointerId = null;
634+
activeResizer = null;
635+
621636
document.body.style.userSelect = '';
622637
localStorage.setItem('sidebarWidth', String($sidebarWidth));
623638
};
624639
625-
const resizeSidebarHandler = (endClientX) => {
640+
const resizeSidebarHandler = (endClientX: number) => {
626641
const dx = endClientX - startClientX;
627642
const newSidebarWidth = Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, startWidth + dx));
628643
629644
sidebarWidth.set(newSidebarWidth);
630645
document.documentElement.style.setProperty('--sidebar-width', `${newSidebarWidth}px`);
631646
};
632647
648+
onDestroy(() => {
649+
if (isResizing) {
650+
document.body.style.userSelect = '';
651+
}
652+
});
653+
633654
onMount(async () => {
634655
try {
635656
const width = Number(localStorage.getItem('sidebarWidth'));
@@ -861,13 +882,13 @@
861882
/>
862883

863884
<svelte:window
864-
on:mousemove={(e) => {
885+
on:pointermove={(e) => {
865886
if (!isResizing) return;
887+
if (activePointerId !== null && e.pointerId !== activePointerId) return;
866888
resizeSidebarHandler(e.clientX);
867889
}}
868-
on:mouseup={() => {
869-
resizeEndHandler();
870-
}}
890+
on:pointerup={resizeEndHandler}
891+
on:pointercancel={resizeEndHandler}
871892
/>
872893

873894
<MobileSwipePanel
@@ -1726,14 +1747,15 @@
17261747

17271748
{#if !$mobile && visible}
17281749
<div
1729-
class="relative flex items-center justify-center group border-l border-gray-50 dark:border-gray-850/30 hover:border-gray-200 dark:hover:border-gray-800 transition z-20"
1750+
class="relative flex items-center justify-center group border-r border-gray-50 dark:border-gray-850/30 hover:border-gray-200 dark:hover:border-gray-800 transition z-20 bg-transparent p-0 appearance-none"
17301751
id="sidebar-resizer"
1731-
on:mousedown={resizeStartHandler}
1752+
on:pointerdown={resizeStartHandler}
17321753
role="separator"
17331754
>
17341755
<div
17351756
class=" absolute -left-1.5 -right-1.5 -top-0 -bottom-0 z-20 cursor-col-resize bg-transparent"
1736-
/>
1757+
style="touch-action: none;"
1758+
></div>
17371759
</div>
17381760
{/if}
17391761
{/if}

src/routes/(app)/automations/+page.svelte

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@
307307
308308
const formatRRule = (rrule: string): string => {
309309
// Detect one-time schedule (ONCE)
310-
if (rrule.includes('COUNT=1')) {
310+
if (/COUNT=1(?!\d)/.test(rrule)) {
311311
const match = rrule.match(/DTSTART:(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})/);
312312
if (match) {
313313
const d = new Date(`${match[1]}-${match[2]}-${match[3]}T${match[4]}:${match[5]}`);
@@ -317,6 +317,9 @@
317317
}
318318
const parts: Record<string, string> = {};
319319
rrule
320+
.split(/\s+/)
321+
.filter((line) => !line.toUpperCase().startsWith('DTSTART'))
322+
.join('')
320323
.replace('RRULE:', '')
321324
.split(';')
322325
.forEach((p) => {

0 commit comments

Comments
 (0)