File size: 3,417 Bytes
814c07e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6c01099
 
814c07e
 
 
 
6c01099
814c07e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6c01099
814c07e
6c01099
 
 
 
 
 
 
 
 
 
 
 
814c07e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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}`,
    };
  }
}