'use client'; import React, { useState, useEffect, useRef } from 'react'; import type { Meta, StoryObj } from "@storybook/react"; import { getPublicClient } from "../api"; const client = getPublicClient('f5d86791-d513-48de-8fd2-b6dbdd4abe9b'); const DEFAULT_PROMPT = `Your job is to predict the next 5-10 words. Never break your role. Write only the text that follows, NOTHING ELSE (this is very important). You must attempt to predict the next words no matter what based on the current text input. Do NOT write anything else or apologize. No more than 5-10 words. Current text: {{inputText}}`; export const AutocompleteTextField = ({ prompt = DEFAULT_PROMPT, placeholder = "", }: any) => { const [text, setText] = React.useState(''); const [dirtyCount, setDirtyCount] = React.useState(0); const [prediction, setPrediction] = React.useState(''); const inputRef = React.useRef(null) as any; const [isFocused, setIsFocused] = useState(false); const timeoutRef = useRef(null) as any; // Event handler for focus const handleFocus = () => { setIsFocused(true); }; // Event handler for blur const handleBlur = () => { setIsFocused(false); }; useEffect(() => { if (!inputRef.current) { return; } const currentElement = inputRef.current as HTMLElement; // Attach the event listeners currentElement.addEventListener('focus', handleFocus); currentElement.addEventListener('blur', handleBlur); // Cleanup function to remove event listeners return () => { currentElement.removeEventListener('focus', handleFocus); currentElement.removeEventListener('blur', handleBlur); }; }, []); // Empty dependency array ensures this effect runs only once React.useEffect(() => { if (dirtyCount > 0) { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } if (text !== '') { timeoutRef.current = setTimeout(() => { getPredictions(text); setDirtyCount(0); }, 500); } } }, [dirtyCount, text]); const handleInput = (e: any) => { e.preventDefault(); console.log(e.target.innerHTML); setText(() => { let newText = e.target.innerHTML; newText = newText.replace(/(){2,}/gi, '
'); newText = newText.replace(//gi, '\n'); newText = newText.replace(/ /gi, ' '); const lastChar = newText[newText.length - 1] === ' ' ? ' ' : newText[newText.length - 1]; if (lastChar === prediction[0]) { setPrediction((prevPrediction) => { const newPrediction = prevPrediction.slice(1); return newPrediction; }); } else { setDirtyCount((prevCount) => prevCount + 1); setPrediction(''); } return newText; }); }; const handleKeyDown = (e: any) => { if (e.key === 'Tab' && prediction) { e.preventDefault(); // Append prediction and reset prediction state const newText = text + prediction; setText(newText); setPrediction(''); const html = newText.split('\n').join('
'); inputRef.current.innerHTML = html; // Update contentEditable text const range = document.createRange(); const sel = window.getSelection() as any; range.selectNodeContents(inputRef.current); // Select the entire content of the div range.collapse(false); // false collapses the range to the end, true to the start sel.removeAllRanges(); // Remove any existing selections sel.addRange(range); // Add the new range } else if (e.key === 'Escape') { setPrediction(''); } }; const getPredictions = async (inputText: string) => { if (!client) { console.error("OpenAI client is not defined."); return; } try { const response = await client.getCompletion({ model: 'gpt-3.5-turbo', messages: [{ role: 'system', content: prompt.replace("{{inputText}}", inputText), }], }); if (response && response.choices && response.choices.length > 0) { const predictedText = response.choices[0].message.content; console.log("Predicted text:", predictedText); setPrediction(predictedText); } } catch (error) { console.error("Failed to fetch predictions:", error); } }; return (
{ inputRef.current.focus(); }} > { inputRef.current.focus(); }} >{prediction.split('\n').join('
')}
{ text ? '' : placeholder }
); }; function TextCompleterStory() { return (

Autocomplete Text Field

); } //👇 This default export determines where your story goes in the story list const meta: Meta = { component: TextCompleterStory, }; export default meta; export const DefaultStory: StoryObj = { // harded code values for now 😬 args: {}, };