From 54050b543a122853800eba77a280be66f2d330bd Mon Sep 17 00:00:00 2001 From: N0VA Date: Tue, 6 Jan 2026 21:17:10 +0100 Subject: [PATCH] New UI --- ui/index.html | 151 +++++++++++++++-------- ui/script.js | 51 +++++--- ui/style.css | 333 ++++++++++++++++++++++++++++++++------------------ 3 files changed, 352 insertions(+), 183 deletions(-) diff --git a/ui/index.html b/ui/index.html index 8effd7b..697b6c3 100644 --- a/ui/index.html +++ b/ui/index.html @@ -1,54 +1,109 @@ - - + - - - - Stupid LLM Editor - - - -
-
-

Stupid LLM Editor

-

AI Pair-Programmer for Polish Literature

-
+ + + + Kreatyw + + + +
+ + +
+
+
+
+ +
-
- - -
-
-
- -
+
- -
-
- -
- - - - - + + diff --git a/ui/script.js b/ui/script.js index 3a24f4d..249fd92 100644 --- a/ui/script.js +++ b/ui/script.js @@ -1,10 +1,13 @@ + document.addEventListener('DOMContentLoaded', () => { const editor = document.getElementById('editor'); const suggestionOverlay = document.getElementById('suggestion-overlay'); const status = document.getElementById('status'); + const statusIndicator = document.querySelector('.status-indicator'); // Controls const nGramSelect = document.getElementById('n-gram'); + const nValDisplay = document.getElementById('n-val'); const tempInput = document.getElementById('temperature'); const tempValDisplay = document.getElementById('temp-val'); const lengthInput = document.getElementById('length'); @@ -15,17 +18,21 @@ document.addEventListener('DOMContentLoaded', () => { let isFetching = false; let debounceTimer; - const autoResize = () => { - editor.style.height = 'auto'; - suggestionOverlay.style.height = 'auto'; - const newHeight = Math.max(360, editor.scrollHeight); - editor.style.height = newHeight + 'px'; - suggestionOverlay.style.height = newHeight + 'px'; + // --- UI Logic --- + + const updateUI = () => { + nValDisplay.textContent = nGramSelect.value; + tempValDisplay.textContent = tempInput.value; + lengthValDisplay.textContent = lengthInput.value; }; - tempInput.addEventListener('input', () => { tempValDisplay.textContent = tempInput.value; }); - lengthInput.addEventListener('input', () => { lengthValDisplay.textContent = lengthInput.value; }); - + tempInput.addEventListener('input', updateUI); + lengthInput.addEventListener('input', updateUI); + nGramSelect.addEventListener('change', () => { + updateUI(); + triggerUpdate(); + }); + const triggerUpdate = () => { currentSuggestion = ''; updateSuggestion(); @@ -33,16 +40,17 @@ document.addEventListener('DOMContentLoaded', () => { if (prompt.trim().length > 0) fetchPrediction(prompt); }; - nGramSelect.addEventListener('change', triggerUpdate); tempInput.addEventListener('change', triggerUpdate); lengthInput.addEventListener('change', triggerUpdate); + // --- Core Functions --- + const fetchPrediction = async (prompt, customLength = null) => { if (isFetching) return; isFetching = true; status.textContent = 'Thinking...'; - status.classList.add('fetching'); + statusIndicator.classList.add('fetching'); const n = parseInt(nGramSelect.value); const temperature = parseFloat(tempInput.value); @@ -72,7 +80,7 @@ document.addEventListener('DOMContentLoaded', () => { } finally { isFetching = false; status.textContent = 'Idle'; - status.classList.remove('fetching'); + statusIndicator.classList.remove('fetching'); } }; @@ -88,15 +96,18 @@ document.addEventListener('DOMContentLoaded', () => { editor.value += space + text; currentSuggestion = ''; updateSuggestion(); - autoResize(); - window.scrollTo(0, document.body.scrollHeight); + + // Ensure the editor scrolls with content + editor.scrollTop = editor.scrollHeight; }; + // --- Event Handlers --- + editor.addEventListener('input', () => { - autoResize(); clearTimeout(debounceTimer); currentSuggestion = ''; updateSuggestion(); + const prompt = editor.value; if (prompt.trim().length === 0) return; debounceTimer = setTimeout(() => fetchPrediction(prompt), 300); @@ -114,5 +125,11 @@ document.addEventListener('DOMContentLoaded', () => { fetchPrediction(editor.value, 50); }); - autoResize(); -}); \ No newline at end of file + // Sync scroll + editor.addEventListener('scroll', () => { + suggestionOverlay.scrollTop = editor.scrollTop; + }); + + // Initialize UI badges + updateUI(); +}); diff --git a/ui/style.css b/ui/style.css index 2af10ba..9ec3be0 100644 --- a/ui/style.css +++ b/ui/style.css @@ -1,168 +1,265 @@ - :root { - --background: hsl(0 0% 3.9%); - --foreground: hsl(0 0% 98%); - --card: hsl(0 0% 12%); - --card-foreground: hsl(0 0% 98%); - --popover: hsl(0 0% 3.9%); - --popover-foreground: hsl(0 0% 98%); - --primary: hsl(0 0% 98%); - --primary-foreground: hsl(0 0% 9%); - --secondary: hsl(0 0% 14.9%); - --secondary-foreground: hsl(0 0% 98%); - --muted: hsl(0 0% 14.9%); - --muted-foreground: hsl(0 0% 63.9%); - --accent: hsl(0 0% 14.9%); - --accent-foreground: hsl(0 0% 98%); - --border: hsl(0 0% 14.9%); - --input: hsl(0 0% 14.9%); - --ring: hsl(0 0% 83.1%); + --background: #09090b; + --foreground: #fafafa; + --card: #09090b; + --card-foreground: #fafafa; + --primary: #fafafa; + --primary-foreground: #18181b; + --secondary: #27272a; + --secondary-foreground: #fafafa; + --muted: #27272a; + --muted-foreground: #a1a1aa; + --accent: #27272a; + --accent-foreground: #fafafa; + --border: #27272a; + --input: #27272a; + --ring: #d4d4d8; --radius: 0.5rem; } +* { + box-sizing: border-box; +} + body { background-color: var(--background); color: var(--foreground); - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; margin: 0; - padding: 2rem; + height: 100vh; + overflow: hidden; } -.container { - max-width: 800px; - margin: 0 auto; +.app-layout { display: flex; - flex-direction: column; - gap: 1.5rem; + height: 100vh; + width: 100vw; } -.header { - text-align: center; -} -.header-title { - font-size: 2rem; - font-weight: 700; - letter-spacing: -0.02em; - margin: 0; -} -.header-subtitle { - color: var(--muted-foreground); - font-size: 1rem; - margin-top: 0.25rem; -} - -.card { +/* Sidebar */ +.sidebar { + width: 300px; background-color: var(--card); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 1.5rem; -} - -.controls { + border-right: 1px solid var(--border); display: flex; flex-direction: column; - gap: 1.5rem; -} -.control-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 1rem; + padding: 1.5rem; + flex-shrink: 0; } -.control-group { +.sidebar-header { + margin-bottom: 2.5rem; +} + +.brand-wrapper { + display: flex; + align-items: center; + gap: 0.6rem; +} + +.brand { + font-size: 1.25rem; + font-weight: 600; + margin: 0; + letter-spacing: -0.02em; +} + +.brand-sub { + font-size: 0.75rem; + color: var(--muted-foreground); + margin: 0.2rem 0 0 0; +} + +.settings { + display: flex; + flex-direction: column; + gap: 2rem; + flex-grow: 1; +} + +.setting-item { display: flex; flex-direction: column; gap: 0.75rem; } + +.setting-label { + display: flex; + justify-content: space-between; + align-items: center; +} + label { font-size: 0.875rem; font-weight: 500; -} -.input-base { - background-color: var(--background); - border: 1px solid var(--border); - border-radius: calc(var(--radius) - 2px); color: var(--foreground); - padding: 0.5rem 0.75rem; - height: 2.5rem; -} -select.input-base { - -webkit-appearance: none; - appearance: none; - padding-right: 2rem; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='hsl(0 0% 63.9%)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3e%3cpath d='M2 5l6 6 6-6'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right 0.5rem center; - background-size: 1em 1em; } -.generate-action { - border-top: 1px solid var(--border); - padding-top: 1.5rem; - text-align: right; +.value-badge { + font-family: monospace; + font-size: 0.75rem; + background: var(--secondary); + padding: 2px 6px; + border-radius: 4px; + color: var(--muted-foreground); } + +/* Styled Inputs */ +.select-input { + background-color: var(--background); + border: 1px solid var(--border); + border-radius: var(--radius); + color: var(--foreground); + padding: 0.5rem; + font-size: 0.875rem; + outline: none; + cursor: pointer; + transition: border-color 0.2s; +} + +.select-input:focus { + border-color: var(--ring); +} + +/* Custom Slider Styling */ +.slider { + -webkit-appearance: none; + width: 100%; + height: 4px; + background: var(--secondary); + border-radius: 2px; + outline: none; +} + +.slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + background: var(--primary); + border-radius: 50%; + cursor: pointer; + border: 2px solid var(--background); + box-shadow: 0 0 0 1px var(--border); +} + +.slider::-moz-range-thumb { + width: 16px; + height: 16px; + background: var(--primary); + border-radius: 50%; + cursor: pointer; + border: 2px solid var(--background); + box-shadow: 0 0 0 1px var(--border); +} + +/* Button */ .btn { - display: inline-flex; - align-items: center; - justify-content: center; - border-radius: calc(var(--radius) - 2px); + width: 100%; + padding: 0.6rem; + border-radius: var(--radius); font-size: 0.875rem; font-weight: 600; - padding: 0.5rem 1rem; - transition: all 0.2s; cursor: pointer; + transition: opacity 0.2s; border: none; } + .btn-primary { background-color: var(--primary); color: var(--primary-foreground); } + .btn-primary:hover { - background-color: hsl(0 0% 98% / 0.9); + opacity: 0.9; } -.editor-wrapper { - position: relative; - padding: 0; +/* Sidebar Footer */ +.sidebar-footer { + padding-top: 1rem; + border-top: 1px solid var(--border); } + +.status-indicator { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.75rem; + color: var(--muted-foreground); +} + +.status-dot { + width: 6px; + height: 6px; + background-color: #22c55e; + border-radius: 50%; +} + +.fetching .status-dot { + background-color: #eab308; + box-shadow: 0 0 8px #eab308; + animation: pulse 1.5s infinite; +} + +@keyframes pulse { + 0% { opacity: 1; } + 50% { opacity: 0.4; } + 100% { opacity: 1; } +} + +/* Main Editor Area */ +.editor-main { + flex-grow: 1; + padding: 2rem; + display: flex; + justify-content: center; + background-color: var(--background); +} + +.editor-container { + width: 100%; + max-width: 800px; + height: 100%; +} + +.editor-viewport { + position: relative; + height: 100%; + border: 1px solid var(--border); + border-radius: var(--radius); + background-color: var(--card); + overflow-y: auto; +} + #editor, #suggestion-overlay { + width: 100%; + height: 100%; + padding: 2rem; + font-family: "SF Mono", "Fira Code", monospace; + font-size: 1.1rem; + line-height: 1.8; + border: none; + background: transparent; + white-space: pre-wrap; + word-wrap: break-word; + margin: 0; + box-sizing: border-box; +} + +#editor { + position: relative; + z-index: 2; + color: var(--foreground); + outline: none; + resize: none; +} + +#suggestion-overlay { position: absolute; top: 0; left: 0; - width: 100%; - height: 100%; - box-sizing: border-box; - font-family: 'SF Mono', 'Fira Code', 'Courier New', monospace; - font-size: 1rem; - line-height: 1.7; - background-color: transparent; - border: none; - overflow: hidden; - resize: none; - padding: 1.5rem; - min-height: 300px; -} -#editor { - z-index: 2; - color: var(--foreground); -} -#editor:focus { outline: none; } -#editor::placeholder { color: var(--muted-foreground); } - -#suggestion-overlay { z-index: 1; color: var(--muted-foreground); pointer-events: none; -} - -.status-bar { - text-align: center; - font-size: 0.8rem; - color: var(--muted-foreground); - display: flex; - gap: 0.5rem; - justify-content: center; -} -#status.fetching { - color: var(--foreground); -} + opacity: 0.5; +} \ No newline at end of file