diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index fd9ca1d54e..c18160331a 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -168,6 +168,13 @@ (history.currentId && history.messages[history.currentId]?.done != true) || generating); $: canCompact = !!history?.currentId; + $: canToggleTemporary = + !embedded && + !chatId && + ($_user?.role === 'admin' || + ($_user?.role === 'user' && + ($_user?.permissions?.chat?.temporary ?? true) && + !($_user?.permissions?.chat?.temporary_enforced ?? false))); export let prompt = ''; export let files = []; @@ -539,6 +546,26 @@ chatInputElement?.replaceCommandWithText(text); }; + const temporaryHandler = async () => { + if (!canToggleTemporary) return; + + if (($settings?.temporaryChatByDefault ?? false) && $temporaryChatEnabled) { + await temporaryChatEnabled.set(null); + } else { + await temporaryChatEnabled.set(!$temporaryChatEnabled); + } + + if (location.pathname !== '/') { + await goto('/'); + } + + if ($temporaryChatEnabled) { + window.history.replaceState(null, '', '?temporary-chat=true'); + } else { + window.history.replaceState(null, '', location.pathname); + } + }; + const insertTextAtCursor = async (text: string) => { const chatInput = document.getElementById('chat-input'); if (!chatInput) return; @@ -1281,7 +1308,7 @@ return; } - if (['compact', 'fork', 'status', 'model'].includes(props?.id)) { + if (['compact', 'fork', 'status', 'model', 'settings', 'temporary'].includes(props?.id)) { editor.chain().focus().deleteRange(range).run(); return; } @@ -1307,11 +1334,15 @@ !!history?.currentId && ($_user?.role === 'admin' || ($_user?.permissions?.chat?.import ?? true)), forkDisabled: () => isActive, + canTemporary: () => canToggleTemporary, + temporaryEnabled: () => $temporaryChatEnabled === true, contextUsage: () => statusContextUsage, onCompact: compactHandler, onStatus: statusHandler, onFork: forkHandler, onModel: () => modelSelector?.open(), + onSettings: () => showSettings.set(true), + onTemporary: temporaryHandler, onSelect: (e) => { const { type, data } = e; diff --git a/src/lib/components/chat/MessageInput/CommandSuggestionList.svelte b/src/lib/components/chat/MessageInput/CommandSuggestionList.svelte index bc561bac11..deea60f895 100644 --- a/src/lib/components/chat/MessageInput/CommandSuggestionList.svelte +++ b/src/lib/components/chat/MessageInput/CommandSuggestionList.svelte @@ -21,12 +21,16 @@ export let onStatus: () => void = () => {}; export let onFork: () => void = () => {}; export let onModel: () => void = () => {}; + export let onSettings: () => void = () => {}; + export let onTemporary: () => void = () => {}; export let insertTextHandler: (text: string) => void = () => {}; export let canCompact: boolean | (() => boolean) = false; export let compactDisabled: boolean | (() => boolean) = false; export let canStatus: boolean | (() => boolean) = false; export let canFork: boolean | (() => boolean) = false; export let forkDisabled: boolean | (() => boolean) = false; + export let canTemporary: boolean | (() => boolean) = false; + export let temporaryEnabled: boolean | (() => boolean) = false; export let contextUsage = null; $: compactAvailable = typeof canCompact === 'function' ? canCompact() : canCompact; @@ -35,6 +39,9 @@ $: statusAvailable = typeof canStatus === 'function' ? canStatus() : canStatus; $: forkAvailable = typeof canFork === 'function' ? canFork() : canFork; $: isForkDisabled = typeof forkDisabled === 'function' ? forkDisabled() : forkDisabled; + $: temporaryAvailable = typeof canTemporary === 'function' ? canTemporary() : canTemporary; + $: isTemporaryEnabled = + typeof temporaryEnabled === 'function' ? temporaryEnabled() : temporaryEnabled; $: resolvedContextUsage = typeof contextUsage === 'function' ? contextUsage() : contextUsage; $: contextHasThreshold = Number(resolvedContextUsage?.threshold) > 0; $: contextPercent = contextHasThreshold @@ -93,6 +100,8 @@ canStatus={statusAvailable} canFork={forkAvailable} forkDisabled={isForkDisabled} + canTemporary={temporaryAvailable} + temporaryEnabled={isTemporaryEnabled} {contextPercent} {contextHasThreshold} onSelect={(e) => { @@ -117,6 +126,12 @@ } else if (type === 'command' && data.id === 'model') { command({ id: data.id, label: data.id }); onModel(); + } else if (type === 'command' && data.id === 'settings') { + command({ id: data.id, label: data.id }); + onSettings(); + } else if (type === 'command' && data.id === 'temporary') { + command({ id: data.id, label: data.id }); + onTemporary(); } else if (type === 'skill') { command({ id: `${data.id}|${data.name}`, diff --git a/src/lib/components/chat/MessageInput/Commands/SlashCommands.svelte b/src/lib/components/chat/MessageInput/Commands/SlashCommands.svelte index e8129776c4..9b438ad2c1 100644 --- a/src/lib/components/chat/MessageInput/Commands/SlashCommands.svelte +++ b/src/lib/components/chat/MessageInput/Commands/SlashCommands.svelte @@ -3,7 +3,10 @@ import { getPrompts } from '$lib/apis/prompts'; import { getSkillItems } from '$lib/apis/skills'; import Tooltip from '$lib/components/common/Tooltip.svelte'; + import ChatBubbleDotted from '$lib/components/icons/ChatBubbleDotted.svelte'; + import ChatBubbleDottedChecked from '$lib/components/icons/ChatBubbleDottedChecked.svelte'; import Cube from '$lib/components/icons/Cube.svelte'; + import Knobs from '$lib/components/icons/Knobs.svelte'; import Sparkles from '$lib/components/icons/Sparkles.svelte'; const i18n = getContext('i18n'); @@ -15,6 +18,8 @@ export let canStatus = false; export let canFork = false; export let forkDisabled = false; + export let canTemporary = false; + export let temporaryEnabled = false; export let contextPercent = 0; export let contextHasThreshold = false; @@ -31,6 +36,9 @@ $: contextCircleOffset = 50.27 * (1 - contextCirclePercent / 100); $: commandItems = [ + ...(canTemporary && 'temporary'.startsWith(query.toLowerCase()) + ? [{ type: 'command', data: { id: 'temporary' } }] + : []), ...(canCompact && 'compact'.startsWith(query.toLowerCase()) ? [{ type: 'command', data: { id: 'compact' } }] : []), @@ -40,7 +48,12 @@ ...(canStatus && 'status'.startsWith(query.toLowerCase()) ? [{ type: 'command', data: { id: 'status' } }] : []), - ...('model'.startsWith(query.toLowerCase()) ? [{ type: 'command', data: { id: 'model' } }] : []) + ...('model'.startsWith(query.toLowerCase()) + ? [{ type: 'command', data: { id: 'model' } }] + : []), + ...('settings'.startsWith(query.toLowerCase()) + ? [{ type: 'command', data: { id: 'settings' } }] + : []) ]; $: filteredPrompts = prompts @@ -128,7 +141,39 @@ {#each commandItems as item, commandIdx} - {#if item.data.id === 'compact'} + {#if item.data.id === 'temporary'} + + + + {:else if item.data.id === 'compact'} + {:else if item.data.id === 'settings'} + + + {/if} {/each} {/if}