<script lang="ts"> import { prompts, user } from '$lib/stores'; import { findWordIndices, getUserPosition, getFormattedDate, getFormattedTime, getCurrentDateTime, getUserTimezone, getWeekday } from '$lib/utils'; import { tick, getContext } from 'svelte'; import { toast } from 'svelte-sonner'; const i18n = getContext('i18n'); export let files; export let prompt = ''; export let command = ''; let selectedPromptIdx = 0; let filteredPrompts = []; $: filteredPrompts = $prompts .filter((p) => p.command.toLowerCase().includes(command.toLowerCase())) .sort((a, b) => a.title.localeCompare(b.title)); $: if (command) { selectedPromptIdx = 0; } export const selectUp = () => { selectedPromptIdx = Math.max(0, selectedPromptIdx - 1); }; export const selectDown = () => { selectedPromptIdx = Math.min(selectedPromptIdx + 1, filteredPrompts.length - 1); }; const confirmPrompt = async (command) => { let text = command.content; if (command.content.includes('{{CLIPBOARD}}')) { const clipboardText = await navigator.clipboard.readText().catch((err) => { toast.error($i18n.t('Failed to read clipboard contents')); return '{{CLIPBOARD}}'; }); const clipboardItems = await navigator.clipboard.read(); let imageUrl = null; for (const item of clipboardItems) { // Check for known image types for (const type of item.types) { if (type.startsWith('image/')) { const blob = await item.getType(type); imageUrl = URL.createObjectURL(blob); } } } if (imageUrl) { files = [ ...files, { type: 'image', url: imageUrl } ]; } text = text.replaceAll('{{CLIPBOARD}}', clipboardText); } if (command.content.includes('{{USER_LOCATION}}')) { const location = await getUserPosition(); text = text.replaceAll('{{USER_LOCATION}}', String(location)); } if (command.content.includes('{{USER_NAME}}')) { console.log($user); const name = $user.name || 'User'; text = text.replaceAll('{{USER_NAME}}', name); } if (command.content.includes('{{USER_LANGUAGE}}')) { const language = localStorage.getItem('locale') || 'en-US'; text = text.replaceAll('{{USER_LANGUAGE}}', language); } if (command.content.includes('{{CURRENT_DATE}}')) { const date = getFormattedDate(); text = text.replaceAll('{{CURRENT_DATE}}', date); } if (command.content.includes('{{CURRENT_TIME}}')) { const time = getFormattedTime(); text = text.replaceAll('{{CURRENT_TIME}}', time); } if (command.content.includes('{{CURRENT_DATETIME}}')) { const dateTime = getCurrentDateTime(); text = text.replaceAll('{{CURRENT_DATETIME}}', dateTime); } if (command.content.includes('{{CURRENT_TIMEZONE}}')) { const timezone = getUserTimezone(); text = text.replaceAll('{{CURRENT_TIMEZONE}}', timezone); } if (command.content.includes('{{CURRENT_WEEKDAY}}')) { const weekday = getWeekday(); text = text.replaceAll('{{CURRENT_WEEKDAY}}', weekday); } prompt = text; const chatInputContainerElement = document.getElementById('chat-input-container'); const chatInputElement = document.getElementById('chat-input'); await tick(); if (chatInputContainerElement) { chatInputContainerElement.style.height = ''; chatInputContainerElement.style.height = Math.min(chatInputContainerElement.scrollHeight, 200) + 'px'; } await tick(); if (chatInputElement) { chatInputElement.focus(); chatInputElement.dispatchEvent(new Event('input')); } }; </script> {#if filteredPrompts.length > 0} <div id="commands-container" class="px-2 mb-2 text-left w-full absolute bottom-0 left-0 right-0 z-10" > <div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850"> <div class="max-h-60 flex flex-col w-full rounded-xl bg-white dark:bg-gray-900 dark:text-gray-100" > <div class="m-1 overflow-y-auto p-1 space-y-0.5 scrollbar-hidden"> {#each filteredPrompts as prompt, promptIdx} <button class=" px-3 py-1.5 rounded-xl w-full text-left {promptIdx === selectedPromptIdx ? ' bg-gray-50 dark:bg-gray-850 selected-command-option-button' : ''}" type="button" on:click={() => { confirmPrompt(prompt); }} on:mousemove={() => { selectedPromptIdx = promptIdx; }} on:focus={() => {}} > <div class=" font-medium text-black dark:text-gray-100"> {prompt.command} </div> <div class=" text-xs text-gray-600 dark:text-gray-100"> {prompt.title} </div> </button> {/each} </div> <div class=" px-2 pt-0.5 pb-1 text-xs text-gray-600 dark:text-gray-100 bg-white dark:bg-gray-900 rounded-b-xl flex items-center space-x-1" > <div> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-3 h-3" > <path stroke-linecap="round" stroke-linejoin="round" d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" /> </svg> </div> <div class="line-clamp-1"> {$i18n.t( 'Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.' )} </div> </div> </div> </div> </div> {/if}