123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- <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}
|