This commit is contained in:
Timothy Jaeryang Baek 2026-10-08 23:21:57 +04:00
parent 91853205e8
commit 0ffd86967f
8 changed files with 91 additions and 53 deletions

View file

@ -24,10 +24,6 @@ ENABLE_MEMORY_SYSTEM_CONTEXT=true
# Set to true to add compact row/column stats to parsed CSV retrieval context.
ENABLE_RAG_CSV_SUMMARY=false
# Opt in to automatic sending of URL-provided prompts (including links from other sites).
# Defaults to prefill only; enabling this requires a backend restart.
ENABLE_URL_QUERY_SUBMISSION=false
# Set to true to preserve backing file records, storage blobs, and per-file vectors when files are removed from knowledge bases.
ENABLE_KNOWLEDGE_FILE_RETENTION=false

View file

@ -1742,8 +1742,6 @@ RESPONSE_WATERMARK = os.getenv('RESPONSE_WATERMARK', '')
IFRAME_CSP = os.getenv('IFRAME_CSP', '')
ENABLE_URL_QUERY_SUBMISSION = os.getenv('ENABLE_URL_QUERY_SUBMISSION', 'False').lower() == 'true'
USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS = (
os.getenv('USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS', 'False').lower() == 'true'
)

View file

@ -53,7 +53,6 @@ from open_webui.config import (
ENABLE_ADMIN_EXPORT,
ENABLE_ONEDRIVE_BUSINESS,
ENABLE_ONEDRIVE_PERSONAL,
ENABLE_URL_QUERY_SUBMISSION,
# OpenAI
ENV,
FRONTEND_BUILD_DIR,
@ -2394,7 +2393,6 @@ async def get_app_config(request: Request):
'enable_code_interpreter': config.get('code_interpreter.enable'),
'enable_image_generation': config.get('image_generation.enable'),
'enable_autocomplete_generation': config.get('task.autocomplete.enable'),
'enable_url_query_submission': ENABLE_URL_QUERY_SUBMISSION,
'enable_community_sharing': config.get('ui.enable_community_sharing'),
'enable_message_rating': config.get('ui.enable_message_rating'),
'enable_user_webhooks': config.get('ui.enable_user_webhooks'),

View file

@ -164,6 +164,7 @@
let navbarElement;
let showEventConfirmation = false;
let pendingUrlActions: { urls: string[]; call: boolean } | null = null;
let eventConfirmationTitle = '';
let eventConfirmationMessage = '';
let eventConfirmationInput = false;
@ -2264,7 +2265,10 @@
});
return () => bridge?.end();
});
beforeNavigate(() => bridge?.end());
beforeNavigate(() => {
bridge?.end();
pendingUrlActions = null;
});
$: if (!$user && (bridge?.connected || bridge?.connecting)) bridge.end();
$: if (selectedModelIds && $models && $config) bridge?.syncModel();
@ -2540,13 +2544,14 @@
taskIds = null;
chatTasks = [];
if ($page.url.searchParams.get('youtube')) {
await uploadWeb(`https://www.youtube.com/watch?v=${$page.url.searchParams.get('youtube')}`);
}
if ($page.url.searchParams.get('load-url')) {
await uploadWeb($page.url.searchParams.get('load-url'));
}
const youtube = $page.url.searchParams.get('youtube');
const loadUrl = $page.url.searchParams.get('load-url');
const urls = [
...(youtube ? [`https://www.youtube.com/watch?v=${youtube}`] : []),
...(loadUrl ? [loadUrl] : [])
];
const call = $page.url.searchParams.get('call') === 'true';
pendingUrlActions = urls.length || call ? { urls, call } : null;
if ($page.url.searchParams.get('web-search') === 'true') {
webSearchEnabled = true;
@ -2583,10 +2588,6 @@
}
}
if ($page.url.searchParams.get('call') === 'true') {
openCallOverlay();
}
// Consume one-shot desktop event (e.g. Spotlight query, call shortcut)
if ($desktopEvent) {
const event = $desktopEvent;
@ -2622,14 +2623,7 @@
} else if ($page.url.searchParams.get('q')) {
const q = $page.url.searchParams.get('q') ?? '';
if (
$config?.features?.enable_url_query_submission &&
($page.url.searchParams.get('submit') ?? 'true') === 'true'
) {
messageInput?.setText(q, () => submitHandler(prompt));
} else {
messageInput?.setText(q);
}
messageInput?.setText(q);
}
selectedModels = selectedModels.map((modelId) =>
@ -4658,6 +4652,35 @@
/>
{/if}
<EventConfirmDialog
show={pendingUrlActions !== null}
title={$i18n.t('Open link')}
on:confirm={async () => {
const actions = pendingUrlActions;
const url = $page.url.href;
pendingUrlActions = null;
if (!actions) return;
for (const source of actions.urls) {
if ($page.url.href !== url) return;
await uploadWeb(source);
}
if (actions.call && $page.url.href === url) openCallOverlay();
}}
on:cancel={() => (pendingUrlActions = null)}
>
<div class="text-sm text-gray-500 space-y-2 max-h-60 overflow-auto break-words">
{#if pendingUrlActions?.urls.length}
<p>{$i18n.t('Load content from these URLs?')}</p>
{#each pendingUrlActions.urls as url}
<p>{url}</p>
{/each}
{/if}
{#if pendingUrlActions?.call}
<p>{$i18n.t('Start a voice call using your microphone?')}</p>
{/if}
</div>
</EventConfirmDialog>
<WebSearchConfirmDialog
bind:show={showWebSearchConfirm}
title={$i18n.t('Use Web Search?')}

View file

@ -382,7 +382,6 @@ type Config = {
enable_functions?: boolean;
enable_tool_servers?: boolean;
enable_autocomplete_generation: boolean;
enable_url_query_submission?: boolean;
enable_direct_connections: boolean;
enable_direct_integrations?: boolean;
enable_version_update_check: boolean;

View file

@ -279,17 +279,10 @@
$config?.features?.enable_tool_servers ? (localStorage.selectedTerminalId ?? null) : null
);
const loadToolServers = setToolServers().catch((e) => {
setToolServers().catch((e) => {
console.error('Failed to load tool servers:', e);
terminalServers.set([]);
});
if (
$config?.features?.enable_url_query_submission &&
$page.url.searchParams.get('q') &&
($page.url.searchParams.get('submit') ?? 'true') === 'true'
) {
await loadToolServers;
}
const setupKeyboardShortcuts = () => {
document.addEventListener('keydown', async (event) => {

View file

@ -1,5 +1,5 @@
<script>
import { onMount } from 'svelte';
import { getContext, onMount } from 'svelte';
import dayjs from '$lib/dayjs';
import { showSidebar } from '$lib/stores';
@ -10,29 +10,41 @@
import Notes from '$lib/components/notes/Notes.svelte';
import Spinner from '$lib/components/common/Spinner.svelte';
import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
const i18n = getContext('i18n');
const title = $page.url.searchParams.get('title') ?? dayjs().format('YYYY-MM-DD');
const content = $page.url.searchParams.get('content') ?? '';
let loaded = false;
let showConfirm = false;
onMount(async () => {
onMount(() => {
if (
$page.url.searchParams.get('content') !== null ||
$page.url.searchParams.get('title') !== null
) {
const title = $page.url.searchParams.get('title') ?? dayjs().format('YYYY-MM-DD');
const content = $page.url.searchParams.get('content') ?? '';
const res = await createNoteHandler(title, content);
if (res) {
goto(`/notes/${res.id}`, { replaceState: true });
}
return;
showConfirm = true;
}
loaded = true;
});
</script>
<ConfirmDialog
bind:show={showConfirm}
title={$i18n.t('Create a new note')}
on:confirm={async () => {
const res = await createNoteHandler(title, content);
if (res) goto(`/notes/${res.id}`, { replaceState: true });
else showConfirm = true;
}}
>
<div class="text-sm text-gray-500 whitespace-pre-wrap break-words max-h-60 overflow-auto">
<div class="font-medium">{title}</div>
{content}
</div>
</ConfirmDialog>
<div
class="flex flex-col w-full h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar
? 'md:max-w-[calc(100%-var(--sidebar-width))]'

View file

@ -1,20 +1,39 @@
<script lang="ts">
import { onMount } from 'svelte';
import { getContext } from 'svelte';
import type { Writable } from 'svelte/store';
import type { i18n as I18n } from 'i18next';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import dayjs from '$lib/dayjs';
import { createNoteHandler } from '$lib/components/notes/utils';
import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
onMount(async () => {
const title = $page.url.searchParams.get('title') ?? dayjs().format('YYYY-MM-DD');
const content = $page.url.searchParams.get('content') ?? '';
const i18n = getContext<Writable<I18n>>('i18n');
const title = $page.url.searchParams.get('title') ?? dayjs().format('YYYY-MM-DD');
const content = $page.url.searchParams.get('content') ?? '';
let showConfirm = true;
const createNote = async () => {
const res = await createNoteHandler(title, content);
if (res) {
goto(`/notes/${res.id}`, { replaceState: true });
} else {
showConfirm = true;
}
});
};
</script>
<ConfirmDialog
bind:show={showConfirm}
title={$i18n.t('Create a new note')}
on:confirm={createNote}
on:cancel={() => goto('/notes', { replaceState: true })}
>
<div class="text-sm text-gray-500 whitespace-pre-wrap break-words max-h-60 overflow-auto">
<div class="font-medium">{title}</div>
{content}
</div>
</ConfirmDialog>