Spaces:
Running
Running
| import type { Example } from './examples'; | |
| import { EXAMPLES, DEFAULT_TOOLS } from './examples'; | |
| export interface UI { | |
| toolsEl: HTMLTextAreaElement; | |
| queryEl: HTMLInputElement; | |
| resultEl: HTMLPreElement; | |
| statusEl: HTMLSpanElement; | |
| examplesEl: HTMLDivElement; | |
| } | |
| export function mountUI(): UI { | |
| const toolsEl = document.getElementById('tools') as HTMLTextAreaElement; | |
| const queryEl = document.getElementById('query') as HTMLInputElement; | |
| const resultEl = document.getElementById('result') as HTMLPreElement; | |
| const statusEl = document.getElementById('status') as HTMLSpanElement; | |
| const examplesEl = document.getElementById('examples') as HTMLDivElement; | |
| // Pre-populate tools textarea with DEFAULT_TOOLS | |
| toolsEl.value = JSON.stringify(DEFAULT_TOOLS, null, 2); | |
| // Render example chips — disabled until the model finishes loading | |
| queryEl.disabled = true; | |
| EXAMPLES.forEach((ex: Example) => { | |
| const chip = document.createElement('button'); | |
| chip.className = 'example-chip'; | |
| chip.textContent = ex.label; | |
| chip.disabled = true; | |
| chip.addEventListener('click', () => { | |
| // Set query value from example | |
| queryEl.value = ex.query; | |
| // Set tools value if example provides custom tools | |
| if (ex.tools) { | |
| toolsEl.value = JSON.stringify(ex.tools, null, 2); | |
| } else { | |
| toolsEl.value = JSON.stringify(DEFAULT_TOOLS, null, 2); | |
| } | |
| // Dispatch change event on queryEl so main.ts's listener fires | |
| queryEl.dispatchEvent(new Event('change', { bubbles: true })); | |
| }); | |
| examplesEl.appendChild(chip); | |
| }); | |
| return { | |
| toolsEl, | |
| queryEl, | |
| resultEl, | |
| statusEl, | |
| examplesEl, | |
| }; | |
| } | |
| export function setStatus(ui: UI, msg: string, busy = false): void { | |
| ui.statusEl.textContent = msg; | |
| ui.statusEl.classList.toggle('busy', busy); | |
| } | |
| /** | |
| * Toggle the disabled state of the example chips and the query input. | |
| * Chips start disabled at mount; main.ts flips them on once the model finishes | |
| * loading. Toggling off again during a busy / failed state is also valid. | |
| */ | |
| export function setInteractiveEnabled(ui: UI, enabled: boolean): void { | |
| ui.queryEl.disabled = !enabled; | |
| ui.examplesEl.querySelectorAll<HTMLButtonElement>('button.example-chip') | |
| .forEach(btn => { btn.disabled = !enabled; }); | |
| } | |
| export function renderResult(ui: UI, rawText: string): void { | |
| // Remove error class | |
| ui.resultEl.classList.remove('error'); | |
| // Try parsing as JSON | |
| try { | |
| const parsed = JSON.parse(rawText); | |
| ui.resultEl.textContent = JSON.stringify(parsed, null, 2); | |
| } catch { | |
| // Not valid JSON, render raw text with note | |
| ui.resultEl.textContent = rawText + '\n(could not parse as JSON)'; | |
| } | |
| } | |
| export function renderError(ui: UI, message: string): void { | |
| ui.resultEl.classList.add('error'); | |
| ui.resultEl.textContent = message; | |
| } | |
| export function readTools( | |
| ui: UI | |
| ): { ok: true; tools: unknown[] } | { ok: false; error: string } { | |
| try { | |
| const parsed = JSON.parse(ui.toolsEl.value); | |
| // Ensure result is an array | |
| if (!Array.isArray(parsed)) { | |
| return { | |
| ok: false, | |
| error: 'Tools must be a JSON array', | |
| }; | |
| } | |
| return { | |
| ok: true, | |
| tools: parsed, | |
| }; | |
| } catch (err) { | |
| const errorMsg = err instanceof Error ? err.message : String(err); | |
| return { | |
| ok: false, | |
| error: `Failed to parse tools JSON: ${errorMsg}`, | |
| }; | |
| } | |
| } | |