123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- <script lang="ts">
- import { prompts } from '$lib/stores';
- import { findWordIndices } 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}}';
- });
- console.log(clipboardText);
- 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);
- console.log(`Image URL (${type}): ${imageUrl}`);
- }
- }
- }
- if (imageUrl) {
- files = [
- ...files,
- {
- type: 'image',
- url: imageUrl
- }
- ];
- }
- text = command.content.replaceAll('{{CLIPBOARD}}', clipboardText);
- }
- prompt = text;
- const chatInputElement = document.getElementById('chat-textarea');
- await tick();
- chatInputElement.style.height = '';
- chatInputElement.style.height = Math.min(chatInputElement.scrollHeight, 200) + 'px';
- chatInputElement?.focus();
- await tick();
- const words = findWordIndices(prompt);
- if (words.length > 0) {
- const word = words.at(0);
- chatInputElement.setSelectionRange(word?.startIndex, word.endIndex + 1);
- }
- };
- </script>
- {#if filteredPrompts.length > 0}
- <div
- id="commands-container"
- class="pl-1 pr-12 mb-3 text-left w-full absolute bottom-0 left-0 right-0 z-10"
- >
- <div class="flex w-full dark:border dark:border-gray-850 rounded-lg">
- <div class=" bg-gray-50 dark:bg-gray-850 w-10 rounded-l-lg text-center">
- <div class=" text-lg font-semibold mt-2">/</div>
- </div>
- <div
- class="max-h-60 flex flex-col w-full rounded-r-lg bg-white dark:bg-gray-900 dark:text-gray-100"
- >
- <div class="m-1 overflow-y-auto p-1 rounded-r-lg 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 pb-1 text-xs text-gray-600 dark:text-gray-100 bg-white dark:bg-gray-900 rounded-br-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}
|