From dd3333ba9ac526a09fe2441ca6721b6abe0d0ec6 Mon Sep 17 00:00:00 2001 From: Hazem Krimi Date: Wed, 15 Jan 2025 01:45:14 +0100 Subject: [PATCH] Add cursor and format code properly --- client/package.json | 2 +- client/src/App.css | 31 ++++++++++------------- client/src/App.jsx | 62 ++++++++++++++++++++++++++++++--------------- server/main.go | 4 +-- 4 files changed, 57 insertions(+), 42 deletions(-) diff --git a/client/package.json b/client/package.json index 8c6a423..69c8ecc 100644 --- a/client/package.json +++ b/client/package.json @@ -8,7 +8,7 @@ "build": "vite build", "lint": "eslint .", "preview": "vite preview", - "format": "prettier -w src" + "format": "prettier -w src" }, "dependencies": { "react": "^18.3.1", diff --git a/client/src/App.css b/client/src/App.css index 38e0009..f4091f5 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -1,7 +1,7 @@ .container { margin: auto; padding: 1rem; - letter-spacing: 0.25rem; + letter-spacing: 0.12rem; } .pending { @@ -9,7 +9,8 @@ } .highlight { - color: white; + color: gray; + animation: blink 0.75s infinite ease-in-out; } .correct { @@ -20,22 +21,16 @@ color: red; } -.enter:before { - content: '\21b5'; - display: inline-block; -} +@keyframes blink { + 0% { + border-left: 1px solid white; + } -.enter:after { - content: ''; - display: block; - clear: right; -} + 50% { + border-left: 1px solid transparent; + } -.tab:before { - content: '\2192'; -} - -.space { - border-bottom-width: 0.15rem; - border-bottom-style: dashed; + 100% { + border-left: 1px solid white; + } } diff --git a/client/src/App.jsx b/client/src/App.jsx index 8386901..7e64fdd 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -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']; function App() { + // TODO: Cleanup the file and create utils for spacing and trimming the code properly. const [code, setCode] = useState(''); const [loaded, setLoaded] = useState(false); const [characters, setCharacters] = useState([]); useEffect(() => { (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 decoder = new TextDecoder(); @@ -21,12 +22,13 @@ function App() { if (done) { setLoaded(true); + setCode(prev => prev.replace(/\s\n/g, '\n')); break; } } })(); }, []); - + useEffect(() => { function handleKeyPress(event) { event.preventDefault(); @@ -34,17 +36,20 @@ function App() { if (!loaded) return; if (characters.length === code.length) 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( characters.concat(event.key === 'Enter') ); - if (code[characters.length] === '\t') + if (/^(\s|\t)$/.test(char)) return setCharacters( - characters.concat(event.key === 'Tab') + characters.concat(['Space', 'Tab'].includes(event.key)) ); setCharacters( - characters.concat(event.key === code[characters.length]) + characters.concat(event.key === char) ); } @@ -55,30 +60,45 @@ function App() { }, [loaded, characters]); function renderCharacterClassName(index) { - if (index === characters.length) return 'highlight' - if (typeof characters[index] === 'undefined') return 'pending' - if (!characters[index]) return 'incorrect' + const typed = characters[index]; - 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) { - if (/^\n$/.test(char)) return 'enter' - if (/^\t$/.test(char)) return 'tab' - if (/^\s$/.test(char)) return 'space' + if (/^\n$/.test(char)) return '\n'; + if (/^(\s|\t)$/.test(char)) return '\u00A0' - return '' + return char; } - function renderClassName(index, char) { - return `${renderCharacterClassName(index)} ${renderSpacingCharacter(char)}`.trim(); + function renderClassName(index) { + return `${renderCharacterClassName(index)}`.trim(); } - return
{code.split('').map((char, index) => ( - {char} - ))}
; + function renderCharacter(char, index) { + const rendered = renderSpacingCharacter(char); + + if (/^\n$/.test(rendered)) { + return
+ } + + return ( + + {rendered} + + ); + } + + return ( +
+ {code.split('').map((char, index) => renderCharacter(char, index))} +
+ ); } export default App; diff --git a/server/main.go b/server/main.go index e4c9a0e..562a2ac 100644 --- a/server/main.go +++ b/server/main.go @@ -41,9 +41,9 @@ func main() { ollamaCtx := context.Background() 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(` - 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 {