Add cursor and format code properly

This commit is contained in:
2025-01-15 01:45:14 +01:00
parent fc07f39796
commit dd3333ba9a
4 changed files with 57 additions and 42 deletions
+10 -15
View File
@@ -1,7 +1,7 @@
.container { .container {
margin: auto; margin: auto;
padding: 1rem; padding: 1rem;
letter-spacing: 0.25rem; letter-spacing: 0.12rem;
} }
.pending { .pending {
@@ -9,7 +9,8 @@
} }
.highlight { .highlight {
color: white; color: gray;
animation: blink 0.75s infinite ease-in-out;
} }
.correct { .correct {
@@ -20,22 +21,16 @@
color: red; color: red;
} }
.enter:before { @keyframes blink {
content: '\21b5'; 0% {
display: inline-block; border-left: 1px solid white;
} }
.enter:after { 50% {
content: ''; border-left: 1px solid transparent;
display: block;
clear: right;
} }
.tab:before { 100% {
content: '\2192'; border-left: 1px solid white;
} }
.space {
border-bottom-width: 0.15rem;
border-bottom-style: dashed;
} }
+40 -20
View File
@@ -4,13 +4,14 @@ import './App.css';
const KEYS_TO_DISABLE = ['Backspace', 'Shift', 'Alt', 'Control', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'Escape', 'Delete', 'PageDown', 'PageUp', 'Home', 'End', 'Insert', 'WakeUp', 'Pause', 'ScrollLock', 'ContextMenu', 'BrowserForward', 'BrowserBack', 'CapsLock', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']; const KEYS_TO_DISABLE = ['Backspace', 'Shift', 'Alt', 'Control', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'Escape', 'Delete', 'PageDown', 'PageUp', 'Home', 'End', 'Insert', 'WakeUp', 'Pause', 'ScrollLock', 'ContextMenu', 'BrowserForward', 'BrowserBack', 'CapsLock', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
function App() { function App() {
// TODO: Cleanup the file and create utils for spacing and trimming the code properly.
const [code, setCode] = useState(''); const [code, setCode] = useState('');
const [loaded, setLoaded] = useState(false); const [loaded, setLoaded] = useState(false);
const [characters, setCharacters] = useState([]); const [characters, setCharacters] = useState([]);
useEffect(() => { useEffect(() => {
(async function() { (async function() {
const response = await fetch('http://localhost:5000/generate?lang=golang&lines=10'); const response = await fetch('http://localhost:5000/generate?lang=julia&lines=10');
const reader = response.body.getReader(); const reader = response.body.getReader();
const decoder = new TextDecoder(); const decoder = new TextDecoder();
@@ -21,6 +22,7 @@ function App() {
if (done) { if (done) {
setLoaded(true); setLoaded(true);
setCode(prev => prev.replace(/\s\n/g, '\n'));
break; break;
} }
} }
@@ -34,17 +36,20 @@ function App() {
if (!loaded) return; if (!loaded) return;
if (characters.length === code.length) return; if (characters.length === code.length) return;
if (KEYS_TO_DISABLE.includes(event.key)) return; if (KEYS_TO_DISABLE.includes(event.key)) return;
if (code[characters.length] === '\n')
const char = code[characters.length];
if (/^\n$/.test(char))
return setCharacters( return setCharacters(
characters.concat(event.key === 'Enter') characters.concat(event.key === 'Enter')
); );
if (code[characters.length] === '\t') if (/^(\s|\t)$/.test(char))
return setCharacters( return setCharacters(
characters.concat(event.key === 'Tab') characters.concat(['Space', 'Tab'].includes(event.key))
); );
setCharacters( setCharacters(
characters.concat(event.key === code[characters.length]) characters.concat(event.key === char)
); );
} }
@@ -55,30 +60,45 @@ function App() {
}, [loaded, characters]); }, [loaded, characters]);
function renderCharacterClassName(index) { function renderCharacterClassName(index) {
if (index === characters.length) return 'highlight' const typed = characters[index];
if (typeof characters[index] === 'undefined') return 'pending'
if (!characters[index]) return 'incorrect'
return 'correct' if (loaded && index === characters.length) return 'highlight';
if (typeof typed === 'undefined') return 'pending';
if (!typed) return 'incorrect';
return 'correct';
} }
// TODO: Remove rendering of spacing characters and render a cursor with the highlight instead.
// TODO: Or look into pretty printing libraries.
function renderSpacingCharacter(char) { function renderSpacingCharacter(char) {
if (/^\n$/.test(char)) return 'enter' if (/^\n$/.test(char)) return '\n';
if (/^\t$/.test(char)) return 'tab' if (/^(\s|\t)$/.test(char)) return '\u00A0'
if (/^\s$/.test(char)) return 'space'
return '' return char;
} }
function renderClassName(index, char) { function renderClassName(index) {
return `${renderCharacterClassName(index)} ${renderSpacingCharacter(char)}`.trim(); return `${renderCharacterClassName(index)}`.trim();
} }
return <div className='container'>{code.split('').map((char, index) => ( function renderCharacter(char, index) {
<span className={renderClassName(index, char)} key={char + index}>{char}</span> const rendered = renderSpacingCharacter(char);
))}</div>;
if (/^\n$/.test(rendered)) {
return <span className={renderClassName(index)} key={char + index}><br /></span>
}
return (
<span className={renderClassName(index)} key={char + index}>
{rendered}
</span>
);
}
return (
<div className='container'>
{code.split('').map((char, index) => renderCharacter(char, index))}
</div>
);
} }
export default App; export default App;
+2 -2
View File
@@ -41,9 +41,9 @@ func main() {
ollamaCtx := context.Background() ollamaCtx := context.Background()
content := []llms.MessageContent{ content := []llms.MessageContent{
llms.TextParts(llms.ChatMessageTypeSystem, `You are only a code generator. You must not respond with anything else but code and do not format with code fences. Always use tabs instead of spaces.`), llms.TextParts(llms.ChatMessageTypeSystem, `You are only a code generator. You must not respond with anything else but code and do not format with code fences. Always use spaces instead of tabs.`),
llms.TextParts(llms.ChatMessageTypeHuman, fmt.Sprintf(` llms.TextParts(llms.ChatMessageTypeHuman, fmt.Sprintf(`
Generate max %d lines of code without any unncessary formatting from a well known open source project in the %s programming language. First line should always be a code comment in the used language in this format: "// Language/Project"`, lines, lang)), Generate max %d lines of code without any unncessary formatting from a well known open source project in the %s programming language.`, lines, lang)),
} }
if _, err := llm.GenerateContent(ollamaCtx, content, llms.WithStreamingFunc(func(streamCtx context.Context, chunk []byte) error { if _, err := llm.GenerateContent(ollamaCtx, content, llms.WithStreamingFunc(func(streamCtx context.Context, chunk []byte) error {