mirror of
https://github.com/CorentinTh/it-tools.git
synced 2024-12-25 09:12:54 +08:00
Merge c93ebb5ef0
into 08d977b8cd
This commit is contained in:
commit
5005545914
5 changed files with 301 additions and 0 deletions
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 a new issue