feat(new tool) AI prompt splitter
This commit is contained in:
		
							parent
							
								
									0b1b98f93e
								
							
						
					
					
						commit
						c93ebb5ef0
					
				
							
								
								
									
										1
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -11,6 +11,7 @@ declare module '@vue/runtime-core' { | ||||
|   export interface GlobalComponents { | ||||
|     '404.page': typeof import('./src/pages/404.page.vue')['default'] | ||||
|     About: typeof import('./src/pages/About.vue')['default'] | ||||
|     AiPromptSplitter: typeof import('./src/tools/ai-prompt-splitter/ai-prompt-splitter.vue')['default'] | ||||
|     App: typeof import('./src/App.vue')['default'] | ||||
|     AsciiTextDrawer: typeof import('./src/tools/ascii-text-drawer/ascii-text-drawer.vue')['default'] | ||||
|     'Base.layout': typeof import('./src/layouts/base.layout.vue')['default'] | ||||
|  | ||||
| @ -392,3 +392,7 @@ tools: | ||||
|   text-to-binary: | ||||
|     title: Text to ASCII binary | ||||
|     description: Convert text to its ASCII binary representation and vice-versa. | ||||
| 
 | ||||
|   ai-prompt-splitter: | ||||
|     title: AI prompt splitter | ||||
|     description: Split up large prompts into smaller, manageable segments based on a set character limit for AI chat prompts. | ||||
|  | ||||
							
								
								
									
										282
									
								
								src/tools/ai-prompt-splitter/ai-prompt-splitter.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								src/tools/ai-prompt-splitter/ai-prompt-splitter.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,282 @@ | ||||
| <script setup lang="ts"> | ||||
| import { computed, ref, watch } from 'vue'; | ||||
| 
 | ||||
| const prompt = ref<string>(''); | ||||
| const characterLimit = ref<number>(15000); | ||||
| const characterLimitInput = ref<number>(characterLimit.value); | ||||
| const splitPrompts = ref<string[]>([]); // Array to store the split prompts | ||||
| const formattedPrompt = ref<string>(''); // To store the entire formatted prompt | ||||
| const clickedParts = ref<boolean[]>([]); // Array to keep track of which parts have been clicked | ||||
| 
 | ||||
| // Watch for changes to characterLimitInput and update characterLimit | ||||
| watch(characterLimitInput, (newLimit) => { | ||||
|   characterLimit.value = newLimit; | ||||
| }); | ||||
| 
 | ||||
| // Computed property to get the current length of the prompt | ||||
| const promptLength = computed(() => prompt.value.length); | ||||
| 
 | ||||
| // Function to split the prompt into parts considering START and END | ||||
| function splitPrompt(): void { | ||||
|   const sentences = prompt.value.split(/(?<=[.!?])\s+|\n/); // Split sentences using punctuation or new lines | ||||
|   const splitPromptsArray: string[] = []; | ||||
|   let currentSplit = ''; | ||||
|   let currentLength = 0; | ||||
|   const numberOfParts = Math.ceil(prompt.value.length / characterLimit.value); // Calculate the total number of parts | ||||
| 
 | ||||
|   sentences.forEach((sentence: string) => { | ||||
|     const sentenceLength = sentence.length; | ||||
| 
 | ||||
|     // Calculate the exact lengths for [START PART X/Y] and [END PART X/Y] | ||||
|     const partIndex = splitPromptsArray.length + 1; | ||||
|     const startTagLength = `[START PART ${partIndex}/${numberOfParts}]`.length; | ||||
|     const endTagLength = `[END PART ${partIndex}/${numberOfParts}]`.length; | ||||
|     const totalTagLength = startTagLength + endTagLength + 2; // +2 for newlines between start/end | ||||
| 
 | ||||
|     // Check if adding this sentence would exceed the character limit | ||||
|     if (currentLength + sentenceLength + totalTagLength > characterLimit.value) { | ||||
|       // Push the current split, trim spaces, and reset the current split and length | ||||
|       splitPromptsArray.push(currentSplit.trim()); | ||||
|       currentSplit = ''; | ||||
|       currentLength = 0; | ||||
|     } | ||||
| 
 | ||||
|     currentSplit += `${sentence} `; // Add the sentence to the current split | ||||
|     currentLength += sentenceLength + 1; // Account for sentence length and a space | ||||
|   }); | ||||
| 
 | ||||
|   // Push any remaining text in the last split | ||||
|   if (currentSplit.length > 0) { | ||||
|     splitPromptsArray.push(currentSplit.trim()); | ||||
|   } | ||||
| 
 | ||||
|   // Set the split prompts array | ||||
|   splitPrompts.value = splitPromptsArray; | ||||
|   clickedParts.value = Array(splitPrompts.value.length).fill(false); // Reset clicked parts on split | ||||
|   formattedPrompt.value = generateFormattedPrompt(); // Update the formatted prompt for display | ||||
| } | ||||
| 
 | ||||
| // Function to generate the complete formatted prompt with [START] and [END] | ||||
| function generateFormattedPrompt(): string { | ||||
|   const numberOfParts = splitPrompts.value.length; | ||||
|   let formatted = `The content I need to share is too large to send in a single message. | ||||
| 
 | ||||
| To make it manageable, I will break it into parts: | ||||
| 
 | ||||
| [START PART 1/${numberOfParts}] | ||||
| this is the content of the part 1 out of ${numberOfParts} in total | ||||
| [END PART 1/${numberOfParts}] | ||||
| 
 | ||||
| Please reply with: "Received part 1/${numberOfParts}" | ||||
| 
 | ||||
| Once I say 'ALL PARTS SENT,' you can start processing my requests.\n\n`; | ||||
| 
 | ||||
|   // Append all split parts with [START] and [END] sections | ||||
|   splitPrompts.value.forEach((split, index) => { | ||||
|     const partIndex = index + 1; | ||||
|     formatted += `[START PART ${partIndex}/${numberOfParts}] | ||||
| ${split} | ||||
| [END PART ${partIndex}/${numberOfParts}]\n\n`; | ||||
|   }); | ||||
| 
 | ||||
|   return formatted; | ||||
| } | ||||
| 
 | ||||
| // Function to copy a specific prompt part to the clipboard | ||||
| function copyToClipboard(part: string, index: number) { | ||||
|   const numberOfParts = splitPrompts.value.length; | ||||
|   const start = `[START PART ${index + 1}/${numberOfParts}]`; | ||||
|   const end = `[END PART ${index + 1}/${numberOfParts}]`; | ||||
|   const fullText = `${start}\n${part}\n${end}`; | ||||
| 
 | ||||
|   navigator.clipboard.writeText(fullText).then(() => { | ||||
|     clickedParts.value[index] = true; // Mark the button as clicked | ||||
|   }).catch((err) => { | ||||
|     console.error('Failed to copy: ', err); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| // Function to copy the initial instructions to the clipboard | ||||
| function copyInstructions() { | ||||
|   const numberOfParts = splitPrompts.value.length; | ||||
|   const initialInstructions = `The content I need to share is too large to send in a single message. | ||||
| 
 | ||||
| To make it manageable, I will break it into parts: | ||||
| 
 | ||||
| [START PART 1/${numberOfParts}] | ||||
| this is the content of the part 1 out of ${numberOfParts} in total | ||||
| [END PART 1/${numberOfParts}] | ||||
| 
 | ||||
| Please reply with: "Received part 1/${numberOfParts}" | ||||
| 
 | ||||
| Once I say 'ALL PARTS SENT,' you can start processing my requests.`; | ||||
| 
 | ||||
|   navigator.clipboard.writeText(initialInstructions).then(() => { | ||||
|   }).catch((err) => { | ||||
|     console.error('Failed to copy instructions: ', err); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| // Function to copy the full prompt to the clipboard | ||||
| function copyFullPrompt() { | ||||
|   const initialPrompt = formattedPrompt.value; | ||||
|   navigator.clipboard.writeText(initialPrompt).then(() => { | ||||
|   }).catch((err) => { | ||||
|     console.error('Failed to copy initial prompt: ', err); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| // Function to copy the final command to the clipboard | ||||
| function copyFinalCommand() { | ||||
|   const finalCommand = 'ALL PARTS SENT'; | ||||
|   navigator.clipboard.writeText(finalCommand).then(() => { | ||||
|   }).catch((err) => { | ||||
|     console.error('Failed to copy final command: ', err); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| // Computed property for potential number of splits based on the current prompt | ||||
| const numberOfSplits = computed(() => { | ||||
|   const sentences = prompt.value.split(/(?<=[.!?])\s+|\n/); | ||||
|   let currentLength = 0; | ||||
|   let splits = 0; | ||||
|   const numberOfParts = Math.ceil(prompt.value.length / characterLimit.value); | ||||
| 
 | ||||
|   sentences.forEach((sentence: string) => { | ||||
|     const sentenceLength = sentence.length; | ||||
|     const partIndex = splits + 1; | ||||
|     const startTagLength = `[START PART ${partIndex}/${numberOfParts}]`.length; | ||||
|     const endTagLength = `[END PART ${partIndex}/${numberOfParts}]`.length; | ||||
|     const totalTagLength = startTagLength + endTagLength + 2; // +2 for newlines | ||||
| 
 | ||||
|     if (currentLength + sentenceLength + totalTagLength > characterLimit.value) { | ||||
|       splits++; // Increment split count if exceeding limit | ||||
|       currentLength = 0; // Reset length for new split | ||||
|     } | ||||
| 
 | ||||
|     currentLength += sentenceLength + 1; // Update length | ||||
|   }); | ||||
| 
 | ||||
|   if (currentLength > 0) { | ||||
|     splits++; // Add an additional split for remaining text | ||||
|   } | ||||
| 
 | ||||
|   return splits; | ||||
| }); | ||||
| 
 | ||||
| // Computed property for formatted prompt display inside c-input-text | ||||
| const formattedPromptDisplay = computed(() => { | ||||
|   if (splitPrompts.value.length > 0) { | ||||
|     return formattedPrompt.value.replace(/\n/g, '\n'); // Display formatted with line breaks | ||||
|   } | ||||
|   return ''; | ||||
| }); | ||||
| 
 | ||||
| // Clear prompt function | ||||
| function clearPrompt(): void { | ||||
|   prompt.value = ''; | ||||
|   splitPrompts.value = []; // Clear the split prompts as well | ||||
|   formattedPrompt.value = ''; // Clear the formatted prompt | ||||
|   clickedParts.value = []; // Reset clicked parts | ||||
| } | ||||
| 
 | ||||
| // Method to restrict character limit input to numbers only | ||||
| function handleCharacterLimitInput(event: KeyboardEvent) { | ||||
|   const allowedKeys = ['Backspace', 'ArrowLeft', 'ArrowRight', 'Tab', 'Control', 'Alt', | ||||
|     'Meta', 'Shift', 'Enter', 'Escape', 'Delete', 'Home', 'End', 'PageUp', 'PageDown']; | ||||
|   if (!allowedKeys.includes(event.key) && (event.key < '0' || event.key > '9')) { | ||||
|     event.preventDefault(); | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <c-card> | ||||
|     <!-- Character Limit Input --> | ||||
|     <n-form-item label="Character Limit"> | ||||
|       <n-input-number | ||||
|         v-model:value="characterLimitInput" | ||||
|         :min="1" | ||||
|         :input-default="characterLimit" | ||||
|         test-id="characterLimitInput" | ||||
|         placeholder="Set character limit..." | ||||
|         @keydown="handleCharacterLimitInput" | ||||
|       /> | ||||
|     </n-form-item> | ||||
| 
 | ||||
|     <!-- Prompt Input with Character Length --> | ||||
|     <c-input-text | ||||
|       v-model:value="prompt" | ||||
|       :label="`Prompt Content (Length: ${promptLength}):`" | ||||
|       placeholder="Enter your prompt here..." | ||||
|       rows="15" | ||||
|       multiline | ||||
|       test-id="promptInput" | ||||
|       raw-text | ||||
|       mb-3 | ||||
|     /> | ||||
|     <!-- Clear and Split Buttons under Full Prompt Label --> | ||||
|     <div style="display: flex; justify-content: space-between; margin-top: 5px;"> | ||||
|       <c-button @click="splitPrompt"> | ||||
|         Split ({{ numberOfSplits }}) | ||||
|       </c-button> | ||||
|       <c-button @click="clearPrompt"> | ||||
|         Clear | ||||
|       </c-button> | ||||
|     </div> | ||||
|   </c-card> | ||||
| 
 | ||||
|   <!-- Split Prompts and Initial Prompt inside c-input-text --> | ||||
|   <c-card :label="`Split Prompts (${numberOfSplits})`"> | ||||
|     <c-input-text | ||||
|       :label="`Split Prompts (${numberOfSplits})`" | ||||
|       :value="formattedPromptDisplay" | ||||
|       placeholder="Your formatted prompt will display here..." | ||||
|       rows="18" | ||||
|       multiline | ||||
|       test-id="splitPromptDisplay" | ||||
|       raw-text | ||||
|     /> | ||||
| 
 | ||||
|     <!-- Copy Instructions, Full Prompt, and Final Command buttons --> | ||||
|     <div class="copy-commands-buttons"> | ||||
|       <c-button @click="copyInstructions"> | ||||
|         Copy Instructions | ||||
|       </c-button> | ||||
|       <c-button float-right @click="copyFullPrompt"> | ||||
|         Copy Full Prompt | ||||
|       </c-button> | ||||
|       <c-button @click="copyFinalCommand"> | ||||
|         Copy Final Command | ||||
|       </c-button> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- Buttons for copying split prompts in a grid layout --> | ||||
|     <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; margin-top: 10px;"> | ||||
|       <div v-for="(split, index) in splitPrompts" :key="index"> | ||||
|         <c-button | ||||
|           style="width: 120px; height: 50px; text-align: center;" | ||||
|           :class="{ 'button-clicked': clickedParts[index] }" | ||||
|           @click="copyToClipboard(split, index)" | ||||
|         > | ||||
|           Copy Part {{ index + 1 }} | ||||
|         </c-button> | ||||
|       </div> | ||||
|     </div> | ||||
|   </c-card> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| .button-clicked { | ||||
|   background-color: #229C60; | ||||
|   color: #000; | ||||
| } | ||||
| 
 | ||||
| .copy-commands-buttons { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   flex-wrap: wrap; | ||||
|   gap: 10px; | ||||
|   margin-top: 15px; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										12
									
								
								src/tools/ai-prompt-splitter/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/ai-prompt-splitter/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| import { IconMessages } from '@tabler/icons-vue'; | ||||
| import { defineTool } from '../tool'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'Ai prompt splitter', | ||||
|   path: '/ai-prompt-splitter', | ||||
|   description: '', | ||||
|   keywords: ['ai', 'prompt', 'splitter'], | ||||
|   component: () => import('./ai-prompt-splitter.vue'), | ||||
|   icon: IconMessages, | ||||
|   createdAt: new Date('2024-10-18'), | ||||
| }); | ||||
| @ -1,6 +1,7 @@ | ||||
| import { tool as base64FileConverter } from './base64-file-converter'; | ||||
| import { tool as base64StringConverter } from './base64-string-converter'; | ||||
| import { tool as basicAuthGenerator } from './basic-auth-generator'; | ||||
| import { tool as aiPromptSplitter } from './ai-prompt-splitter'; | ||||
| import { tool as emailNormalizer } from './email-normalizer'; | ||||
| 
 | ||||
| import { tool as asciiTextDrawer } from './ascii-text-drawer'; | ||||
| @ -184,6 +185,7 @@ export const toolsByCategory: ToolCategory[] = [ | ||||
|       textDiff, | ||||
|       numeronymGenerator, | ||||
|       asciiTextDrawer, | ||||
|       aiPromptSplitter, | ||||
|     ], | ||||
|   }, | ||||
|   { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user