PromptCommands.svelte 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. <script lang="ts">
  2. import { prompts } from '$lib/stores';
  3. import { findWordIndices } from '$lib/utils';
  4. import { tick } from 'svelte';
  5. export let prompt = '';
  6. let selectedCommandIdx = 0;
  7. let filteredPromptCommands = [];
  8. $: filteredPromptCommands = $prompts
  9. .filter((p) => p.command.includes(prompt))
  10. .sort((a, b) => a.title.localeCompare(b.title));
  11. $: if (prompt) {
  12. selectedCommandIdx = 0;
  13. }
  14. export const selectUp = () => {
  15. selectedCommandIdx = Math.max(0, selectedCommandIdx - 1);
  16. };
  17. export const selectDown = () => {
  18. selectedCommandIdx = Math.min(selectedCommandIdx + 1, filteredPromptCommands.length - 1);
  19. };
  20. const confirmCommand = async (command) => {
  21. prompt = command.content;
  22. const chatInputElement = document.getElementById('chat-textarea');
  23. await tick();
  24. chatInputElement.style.height = '';
  25. chatInputElement.style.height = Math.min(chatInputElement.scrollHeight, 200) + 'px';
  26. chatInputElement?.focus();
  27. await tick();
  28. const words = findWordIndices(prompt);
  29. if (words.length > 0) {
  30. const word = words.at(0);
  31. chatInputElement.setSelectionRange(word?.startIndex, word.endIndex + 1);
  32. }
  33. };
  34. </script>
  35. {#if filteredPromptCommands.length > 0}
  36. <div class="md:px-2 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
  37. <div class="flex w-full rounded-lg border border-gray-100 dark:border-gray-700">
  38. <div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
  39. <div class=" text-lg font-semibold mt-2">/</div>
  40. </div>
  41. <div class="max-h-60 flex flex-col w-full rounded-r-lg">
  42. <div class=" overflow-y-auto bg-white p-2 rounded-tr-lg space-y-0.5">
  43. {#each filteredPromptCommands as command, commandIdx}
  44. <button
  45. class=" px-3 py-1.5 rounded-lg w-full text-left {commandIdx === selectedCommandIdx
  46. ? ' bg-gray-100 selected-command-option-button'
  47. : ''}"
  48. type="button"
  49. on:click={() => {
  50. confirmCommand(command);
  51. }}
  52. on:mousemove={() => {
  53. selectedCommandIdx = commandIdx;
  54. }}
  55. on:focus={() => {}}
  56. >
  57. <div class=" font-medium text-black">
  58. {command.command}
  59. </div>
  60. <div class=" text-xs text-gray-600">
  61. {command.title}
  62. </div>
  63. </button>
  64. {/each}
  65. </div>
  66. <div
  67. class=" px-2 pb-1 text-xs text-gray-600 bg-white rounded-br-lg flex items-center space-x-1"
  68. >
  69. <div>
  70. <svg
  71. xmlns="http://www.w3.org/2000/svg"
  72. fill="none"
  73. viewBox="0 0 24 24"
  74. stroke-width="1.5"
  75. stroke="currentColor"
  76. class="w-3 h-3"
  77. >
  78. <path
  79. stroke-linecap="round"
  80. stroke-linejoin="round"
  81. 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"
  82. />
  83. </svg>
  84. </div>
  85. <div class="line-clamp-1">
  86. Tip: Update multiple variable slots consecutively by pressing the tab key in the chat
  87. input after each replacement.
  88. </div>
  89. </div>
  90. </div>
  91. </div>
  92. </div>
  93. {/if}