{ await tick(); }} on:close={() => { integrationsMenuCloseOnOutsideClick = true; }} /> { console.log(content); chatInputElement?.setContent(content?.json ?? null); }} onClose={async () => { await tick(); chatInputElement?.focus(); }} /> {#if loaded}
{#if autoScroll === false && history?.currentId}
{/if}
{ if (inputFiles && inputFiles.length > 0) { const _inputFiles = Array.from(inputFiles); inputFilesHandler(_inputFiles); } else { toast.error($i18n.t(`File not found.`)); } filesInputElement.value = ""; }} />
{ recording = false; await tick(); document.getElementById("chat-input")?.focus(); }} onConfirm={async (data) => { const { text, filename } = data; recording = false; await tick(); await insertTextAtCursor(`${text}`); await tick(); document.getElementById("chat-input")?.focus(); if ($settings?.speechAutoSend ?? false) { dispatch("submit", prompt); } }} />
{ // check if selectedModels support image input dispatch("submit", prompt); }} >
{/if} {#if files.length > 0}
{#each files as file, fileIdx} {#if file.type === "image" || (file?.content_type ?? "").startsWith("image/")} {@const fileUrl = file.url.startsWith( "data", ) || file.url.startsWith("http") ? file.url : `${REXPRO_API_BASE_URL}/files/${file.url}${file?.content_type ? "/content" : ""}`}
{#if atSelectedModel ? visionCapableModels.length === 0 : selectedModels.length !== visionCapableModels.length} !visionCapableModels.includes( id, ), ) .join( ", ", ), }, )} > {/if}
{:else} { // Remove from UI state files.splice( fileIdx, 1, ); files = files; }} on:click={() => { console.log(file); }} /> {/if} {/each}
{/if}
{#if prompt.split("\n").length > 2}
{/if} {#if suggestions} {#key $settings?.richTextInput ?? true} {#key $settings?.showFormattingToolbar ?? false} { prompt = content.md; inputContent = content; command = getCommand(); }} json={true} richText={$settings?.richTextInput ?? true} messageInput={true} showFormattingToolbar={$settings?.showFormattingToolbar ?? false} floatingMenuPlacement={"top-start"} insertPromptAsRichText={$settings?.insertPromptAsRichText ?? false} shiftEnter={!( $settings?.ctrlEnterToSend ?? false ) && !$mobile && !( "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0 )} placeholder={placeholder ? placeholder : $i18n.t( "Send a Message", )} largeTextAsFile={($settings?.largeTextAsFile ?? false) && !shiftKey} autocomplete={$config ?.features ?.enable_autocomplete_generation && ($settings?.promptAutocomplete ?? false)} generateAutoCompletion={async ( text, ) => { if ( selectedModelIds.length === 0 || !selectedModelIds.at( 0, ) ) { toast.error( $i18n.t( "Please select a model first.", ), ); } const res = await generateAutoCompletion( localStorage.token, selectedModelIds.at( 0, ), text, history?.currentId ? createMessagesList( history, history.currentId, ) : null, ).catch( (error) => { console.log( error, ); return null; }, ); console.log(res); return res; }} {suggestions} oncompositionstart={() => (isComposing = true)} oncompositionend={( e, ) => { compositionEndedAt = e.timeStamp; isComposing = false; }} on:keydown={async ( e, ) => { e = e.detail.event; const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac const suggestionsContainerElement = document.getElementById( "suggestions-container", ); if ( e.key === "Escape" ) { stopResponse(); } if ( prompt === "" && e.key == "ArrowUp" ) { e.preventDefault(); const userMessageElement = [ ...document.getElementsByClassName( "user-message", ), ]?.at(-1); if ( userMessageElement ) { userMessageElement.scrollIntoView( { block: "center", }, ); const editButton = [ ...document.getElementsByClassName( "edit-user-message-button", ), ]?.at( -1, ); editButton?.click(); } } if ( !suggestionsContainerElement ) { if ( !$mobile || !( "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0 ) ) { if ( inOrNearComposition( e, ) ) { return; } // Uses keyCode '13' for Enter key for chinese/japanese keyboards. // // Depending on the user's settings, it will send the message // either when Enter is pressed or when Ctrl+Enter is pressed. const enterPressed = ($settings?.ctrlEnterToSend ?? false) ? (e.key === "Enter" || e.keyCode === 13) && isCtrlPressed : (e.key === "Enter" || e.keyCode === 13) && !e.shiftKey; if ( enterPressed ) { e.preventDefault(); if ( prompt !== "" || files.length > 0 ) { dispatch( "submit", prompt, ); } } } } if ( e.key === "Escape" ) { console.log( "Escape", ); atSelectedModel = undefined; selectedToolIds = []; selectedFilterIds = []; webSearchEnabled = false; imageGenerationEnabled = false; codeInterpreterEnabled = false; } }} on:paste={async (e) => { e = e.detail.event; console.log(e); const clipboardData = e.clipboardData || window.clipboardData; if ( clipboardData && clipboardData.items ) { for (const item of clipboardData.items) { if ( item.type === "text/plain" ) { if ( ($settings?.largeTextAsFile ?? false) && !shiftKey ) { const text = clipboardData.getData( "text/plain", ); if ( text.length > PASTED_TEXT_CHARACTER_LIMIT ) { e.preventDefault(); const blob = new Blob( [ text, ], { type: "text/plain", }, ); const file = new File( [ blob, ], `Pasted_Text_${Date.now()}.txt`, { type: "text/plain", }, ); await uploadFileHandler( file, true, { context: "full", }, ); } } } else { const file = item.getAsFile(); if ( file ) { await inputFilesHandler( [ file, ], ); e.preventDefault(); } } } } }} /> {/key} {/key} {/if}
{ filesInputElement.click(); }} uploadGoogleDriveHandler={async () => { try { const fileData = await createPicker(); if (fileData) { const file = new File( [fileData.blob], fileData.name, { type: fileData .blob.type, }, ); await uploadFileHandler( file, ); } else { console.log( "No file was selected from Google Drive", ); } } catch (error) { console.error( "Google Drive Error:", error, ); toast.error( $i18n.t( "Error accessing Google Drive: {{error}}", { error: error.message, }, ), ); } }} uploadOneDriveHandler={async ( authorityType, ) => { try { const fileData = await pickAndDownloadFile( authorityType, ); if (fileData) { const file = new File( [fileData.blob], fileData.name, { type: fileData .blob .type || "application/octet-stream", }, ); await uploadFileHandler( file, ); } else { console.log( "No file was selected from OneDrive", ); } } catch (error) { console.error( "OneDrive Error:", error, ); } }} {onUpload} onClose={async () => { await tick(); const chatInput = document.getElementById( "chat-input", ); chatInput?.focus(); }} > {#if showWebSearchButton || showImageGenerationButton || showCodeInterpreterButton || showToolsButton || showSkillsButton || (toggleFilters && toggleFilters.length > 0)}
{ const { type, id } = e; selectedValvesType = type; selectedValvesItemId = id; showValvesModal = true; integrationsMenuCloseOnOutsideClick = false; }} onClose={async () => { await tick(); const chatInput = document.getElementById( "chat-input", ); chatInput?.focus(); }} > {/if} {#if selectedModelIds.length === 1 && $models.find((m) => m.id === selectedModelIds[0])?.has_user_valves}
{/if}
{#if (selectedToolIds ?? []).length > 0} {/if} {#if (selectedSkillIds ?? []).length > 0} {/if} {#each selectedFilterIds as filterId (filterId)} {@const filter = toggleFilters.find( (f) => f.id === filterId, )} {#if filter} {/if} {/each} {#if webSearchEnabled} {/if} {#if imageGenerationEnabled} {/if} {#if codeInterpreterEnabled} {/if} {#each pendingOAuthTools as pendingTool (pendingTool.id)} {/each}
{#if isActive && prompt === "" && files.length === 0}
{:else} {#if prompt !== "" && !history?.currentId && !$selectedTerminalId && ($config?.features?.enable_notes ?? false) && ($_user?.role === "admin" || ($_user?.permissions?.features?.notes ?? true))} {/if} {#if !history?.currentId || history.messages[history.currentId]?.done == 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} {#if $_user?.role === "admin" || ($_user?.permissions?.chat?.stt ?? true)} {/if} {/if} {#if prompt === "" && files.length === 0 && ($_user?.role === "admin" || ($_user?.permissions?.chat?.call ?? true))}
{:else}
{/if} {/if}
{#if $config?.license_metadata?.input_footer}
{@html DOMPurify.sanitize( marked( $config?.license_metadata?.input_footer, ), )}
{:else}
{/if}
{/if}